mooncat-browser 0.1.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.
Files changed (117) hide show
  1. package/README.md +213 -0
  2. package/browser-op/backend/browserd.cjs +1004 -0
  3. package/browser-op/backend/rpc-client.cjs +64 -0
  4. package/browser-op/backend/state.cjs +51 -0
  5. package/browser-op/cdp/capture-inject.js +426 -0
  6. package/browser-op/cdp/capture-inject.ts +426 -0
  7. package/browser-op/cdp/capture-service.cjs +172 -0
  8. package/browser-op/cdp/chrome-launcher.cjs +370 -0
  9. package/browser-op/cdp/chrome-path.cjs +57 -0
  10. package/browser-op/cdp/state.cjs +89 -0
  11. package/browser-op/extension/extension-detect.cjs +228 -0
  12. package/browser-op/extension/server.cjs +197 -0
  13. package/browser-op/extension/service.cjs +228 -0
  14. package/browser-op/extension/state.cjs +78 -0
  15. package/browser-op/index.cjs +389 -0
  16. package/browser-op/package.json +17 -0
  17. package/browser-op/py/behavior.py +138 -0
  18. package/browser-op/py/browser.py +340 -0
  19. package/browser-op/py/captcha.py +115 -0
  20. package/browser-op/py/crawler.py +125 -0
  21. package/browser-op/py/examples/01_open_and_probe.py +48 -0
  22. package/browser-op/py/examples/02_reuse_and_probe.py +66 -0
  23. package/browser-op/py/examples/03_interact.py +66 -0
  24. package/browser-op/py/find.py +150 -0
  25. package/browser-op/py/honeypot.py +73 -0
  26. package/browser-op/py/humanize.py +392 -0
  27. package/browser-op/py/image.py +186 -0
  28. package/browser-op/py/interact.py +193 -0
  29. package/browser-op/py/markdown.py +38 -0
  30. package/browser-op/py/pyproject.toml +32 -0
  31. package/browser-op/py/ready.py +208 -0
  32. package/browser-op/py/scroll.py +180 -0
  33. package/browser-op/py/upload.py +103 -0
  34. package/browser-op/py/visual_target.py +47 -0
  35. package/browser-op/py/visualize.py +91 -0
  36. package/browser-op/state.cjs +63 -0
  37. package/browser-op/web/behavior.js +153 -0
  38. package/browser-op/web/browser.js +231 -0
  39. package/browser-op/web/captcha.js +85 -0
  40. package/browser-op/web/crawler.js +109 -0
  41. package/browser-op/web/find.js +147 -0
  42. package/browser-op/web/honeypot.js +68 -0
  43. package/browser-op/web/humanize.js +522 -0
  44. package/browser-op/web/image.js +177 -0
  45. package/browser-op/web/interact.js +169 -0
  46. package/browser-op/web/markdown.js +80 -0
  47. package/browser-op/web/ready.js +295 -0
  48. package/browser-op/web/scroll.js +167 -0
  49. package/browser-op/web/upload.js +116 -0
  50. package/browser-op/web/visual-runtime.inject.cjs +6 -0
  51. package/browser-op/webplater/.env.example +7 -0
  52. package/browser-op/webplater/ARCHITECTURE.md +102 -0
  53. package/browser-op/webplater/dist/chrome-mv3/assets/popup-BUZEUmsx.css +1 -0
  54. package/browser-op/webplater/dist/chrome-mv3/background.js +2 -0
  55. package/browser-op/webplater/dist/chrome-mv3/capture.js +310 -0
  56. package/browser-op/webplater/dist/chrome-mv3/chunks/_virtual_wxt-html-plugins-DPbbfBKe.js +1 -0
  57. package/browser-op/webplater/dist/chrome-mv3/chunks/offscreen-CFXYw9Mo.js +1 -0
  58. package/browser-op/webplater/dist/chrome-mv3/chunks/popup-C-lpxZZO.js +1 -0
  59. package/browser-op/webplater/dist/chrome-mv3/content-scripts/content.js +7 -0
  60. package/browser-op/webplater/dist/chrome-mv3/manifest.json +1 -0
  61. package/browser-op/webplater/dist/chrome-mv3/offscreen.html +16 -0
  62. package/browser-op/webplater/dist/chrome-mv3/popup.html +31 -0
  63. package/browser-op/webplater/entrypoints/background.ts +938 -0
  64. package/browser-op/webplater/entrypoints/content.ts +1150 -0
  65. package/browser-op/webplater/entrypoints/offscreen/index.html +15 -0
  66. package/browser-op/webplater/entrypoints/offscreen/main.ts +161 -0
  67. package/browser-op/webplater/entrypoints/popup/index.html +29 -0
  68. package/browser-op/webplater/entrypoints/popup/main.ts +61 -0
  69. package/browser-op/webplater/entrypoints/popup/style.css +100 -0
  70. package/browser-op/webplater/lib/snapshot.ts +352 -0
  71. package/browser-op/webplater/package.json +29 -0
  72. package/browser-op/webplater/pnpm-lock.yaml +3411 -0
  73. package/browser-op/webplater/public/capture.js +310 -0
  74. package/browser-op/webplater/scripts/publish-extension.mjs +176 -0
  75. package/browser-op/webplater/tsconfig.json +19 -0
  76. package/browser-op/webplater/wxt.config.ts +34 -0
  77. package/dist/actions.md +102 -0
  78. package/dist/cli.d.ts +2 -0
  79. package/dist/cli.d.ts.map +1 -0
  80. package/dist/cli.js +278 -0
  81. package/dist/cli.js.map +1 -0
  82. package/dist/client.d.ts +94 -0
  83. package/dist/client.d.ts.map +1 -0
  84. package/dist/client.js +277 -0
  85. package/dist/client.js.map +1 -0
  86. package/dist/config.d.ts +61 -0
  87. package/dist/config.d.ts.map +1 -0
  88. package/dist/config.js +119 -0
  89. package/dist/config.js.map +1 -0
  90. package/dist/protocol.d.ts +195 -0
  91. package/dist/protocol.d.ts.map +1 -0
  92. package/dist/protocol.js +11 -0
  93. package/dist/protocol.js.map +1 -0
  94. package/dist/server.d.ts +66 -0
  95. package/dist/server.d.ts.map +1 -0
  96. package/dist/server.js +259 -0
  97. package/dist/server.js.map +1 -0
  98. package/package.json +78 -0
  99. package/schemas/browser.clearCookies.schema.json +13 -0
  100. package/schemas/browser.close.schema.json +9 -0
  101. package/schemas/browser.getCookies.schema.json +13 -0
  102. package/schemas/browser.getDownload.schema.json +15 -0
  103. package/schemas/browser.health.schema.json +9 -0
  104. package/schemas/browser.listDownloads.schema.json +16 -0
  105. package/schemas/browser.listTabs.schema.json +9 -0
  106. package/schemas/browser.newTab.schema.json +15 -0
  107. package/schemas/browser.open.schema.json +15 -0
  108. package/schemas/browser.operate.schema.json +15 -0
  109. package/schemas/browser.reuseTab.schema.json +15 -0
  110. package/schemas/browser.setCookies.schema.json +15 -0
  111. package/schemas/browser.waitFor.schema.json +15 -0
  112. package/schemas/browser.waitForDownload.schema.json +15 -0
  113. package/skills/browser/SKILL.md +110 -0
  114. package/skills/browser/references/collect.md +163 -0
  115. package/skills/browser/references/high-risk.md +161 -0
  116. package/skills/browser/references/operate-actions.md +92 -0
  117. package/skills/browser/references/probing.md +302 -0
@@ -0,0 +1,15 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>WebPlater Offscreen</title>
6
+ </head>
7
+ <body>
8
+ <!--
9
+ offscreen document:常驻宿主,持有到 browserd 的 WebSocket 长连接。
10
+ 页面本身无 UI,仅承载 main.ts 的 WS 桥接逻辑。
11
+ 由 background.ts 的 ensureOffscreen() 创建。
12
+ -->
13
+ <script type="module" src="./main.ts"></script>
14
+ </body>
15
+ </html>
@@ -0,0 +1,161 @@
1
+ // offscreen main — 持有到 browserd 的 WebSocket 长连接,把 host 命令桥接到 background。
2
+ //
3
+ // 动机:MV3 service worker 会被 Chrome 回收(约 30s 空闲,单事件最长 5min),
4
+ // 把 WS 长连接放在 SW 里会随回收断开、长任务中断。offscreen document 常驻到关闭,
5
+ // 是 WS 的可靠宿主。
6
+ //
7
+ // 命令链路:
8
+ // browserd :17321 ⇿ offscreen (WS) ⇿ background (chrome.runtime message)
9
+ //
10
+ // background 负责 ensureOffscreen() 建本 document;本文件只做 WS 持有 + 双向桥接。
11
+ //
12
+ // 注意:本文件作为 offscreen/index.html 的内嵌 module script,必须用顶层副作用执行
13
+ // (与 popup/main.ts 一致)。不要用 `export default defineUnlistedScript`——那会被
14
+ // wxt 识别成独立 unlisted-script 入口并从本 html 抽走,导致脚本不被加载。
15
+
16
+ interface CommandMessage {
17
+ id: number
18
+ type: 'command'
19
+ method: string
20
+ params?: Record<string, unknown>
21
+ }
22
+
23
+ interface ResponseMessage {
24
+ id: number
25
+ type: 'response'
26
+ ok: boolean
27
+ result?: unknown
28
+ error?: string
29
+ }
30
+
31
+ interface BackgroundResponse {
32
+ ok: boolean
33
+ result?: unknown
34
+ error?: string
35
+ }
36
+
37
+ const HOST_WS_URL = 'ws://127.0.0.1:17321'
38
+ const RECONNECT_DELAY_MS = 1000
39
+ const PING_INTERVAL_MS = 20000
40
+
41
+ let socket: WebSocket | null = null
42
+ let reconnectTimer: ReturnType<typeof setTimeout> | null = null
43
+ let pingTimer: ReturnType<typeof setInterval> | null = null
44
+
45
+ function connect() {
46
+ if (socket?.readyState === WebSocket.CONNECTING || socket?.readyState === WebSocket.OPEN) {
47
+ return
48
+ }
49
+
50
+ socket = new WebSocket(HOST_WS_URL)
51
+
52
+ socket.addEventListener('open', () => {
53
+ if (reconnectTimer) {
54
+ clearTimeout(reconnectTimer)
55
+ reconnectTimer = null
56
+ }
57
+ console.log('[WebPlater/offscreen] connected to host', HOST_WS_URL)
58
+ // hello 只需 type:host server.cjs 只看消息 type 判活,name/version/extensionId
59
+ // 是装饰字段,且 chrome.runtime.getManifest/id 在 offscreen 不可用,故不取。
60
+ send({ type: 'hello', name: 'bee-web-plugin' })
61
+ startPing()
62
+ })
63
+
64
+ socket.addEventListener('message', async (event) => {
65
+ let msg: CommandMessage | undefined
66
+ try {
67
+ msg = JSON.parse(String(event.data))
68
+ } catch {
69
+ console.error('[WebPlater/offscreen] invalid JSON from host', event.data)
70
+ return
71
+ }
72
+ if (!msg || msg.type !== 'command' || typeof msg.id !== 'number') return
73
+
74
+ try {
75
+ const result = await sendToBackground(msg.id, msg.method, msg.params ?? {})
76
+ respond(msg.id, { ok: true, result })
77
+ } catch (err) {
78
+ respond(msg.id, {
79
+ ok: false,
80
+ error: err instanceof Error ? err.message : String(err),
81
+ })
82
+ }
83
+ })
84
+
85
+ socket.addEventListener('close', () => {
86
+ console.log('[WebPlater/offscreen] disconnected, reconnecting...')
87
+ socket = null
88
+ stopPing()
89
+ scheduleReconnect()
90
+ })
91
+
92
+ socket.addEventListener('error', () => {
93
+ // close 事件会随后触发,由它负责重连
94
+ socket?.close()
95
+ })
96
+ }
97
+
98
+ function send(payload: unknown): void {
99
+ if (socket?.readyState === WebSocket.OPEN) {
100
+ socket.send(JSON.stringify(payload))
101
+ }
102
+ }
103
+
104
+ function respond(id: number, response: { ok: boolean; result?: unknown; error?: string }): void {
105
+ const msg: ResponseMessage = { id, type: 'response', ...response }
106
+ send(msg)
107
+ }
108
+
109
+ /**
110
+ * 把命令转发给 background 执行并等回包。
111
+ * chrome.runtime.sendMessage 会唤醒休眠的 service worker;SW 在 message handler
112
+ * pending(sendResponse 未调用)期间保持活跃,长任务(如 evaluate 300s)不会因 SW
113
+ * 回收而中断连接本身(连接在本 offscreen 持有)。
114
+ */
115
+ function sendToBackground(
116
+ id: number,
117
+ method: string,
118
+ params: Record<string, unknown>,
119
+ ): Promise<unknown> {
120
+ return new Promise((resolve, reject) => {
121
+ chrome.runtime.sendMessage(
122
+ { source: 'offscreen-command', id, method, params },
123
+ (response) => {
124
+ if (chrome.runtime.lastError) {
125
+ reject(new Error(chrome.runtime.lastError.message))
126
+ return
127
+ }
128
+ const r = response as BackgroundResponse | undefined
129
+ if (r && typeof r === 'object' && r.ok === false) {
130
+ reject(new Error(r.error ?? 'background error'))
131
+ return
132
+ }
133
+ resolve(r?.result)
134
+ },
135
+ )
136
+ })
137
+ }
138
+
139
+ function startPing(): void {
140
+ stopPing()
141
+ pingTimer = setInterval(() => {
142
+ send({ type: 'ping', time: Date.now() })
143
+ }, PING_INTERVAL_MS)
144
+ }
145
+
146
+ function stopPing(): void {
147
+ if (pingTimer) {
148
+ clearInterval(pingTimer)
149
+ pingTimer = null
150
+ }
151
+ }
152
+
153
+ function scheduleReconnect(): void {
154
+ if (reconnectTimer) return
155
+ reconnectTimer = setTimeout(() => {
156
+ reconnectTimer = null
157
+ connect()
158
+ }, RECONNECT_DELAY_MS)
159
+ }
160
+
161
+ connect()
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>WebPlater</title>
7
+ </head>
8
+ <body>
9
+ <div id="app">
10
+ <header>
11
+ <h1>WebPlater</h1>
12
+ <p id="status" class="status">checking...</p>
13
+ </header>
14
+
15
+ <section>
16
+ <h2>调试</h2>
17
+ <div class="row">
18
+ <button id="btn-tabs">List Tabs</button>
19
+ <button id="btn-status">Page Status</button>
20
+ <button id="btn-reconnect">Reconnect</button>
21
+ </div>
22
+ </section>
23
+
24
+ <pre id="result">Ready</pre>
25
+ </div>
26
+
27
+ <script type="module" src="./main.ts"></script>
28
+ </body>
29
+ </html>
@@ -0,0 +1,61 @@
1
+ import './style.css'
2
+
3
+ const result = document.getElementById('result') as HTMLPreElement
4
+ const statusEl = document.getElementById('status') as HTMLParagraphElement
5
+
6
+ function render(data: unknown): void {
7
+ result.textContent = typeof data === 'string' ? data : JSON.stringify(data, null, 2)
8
+ }
9
+
10
+ // popup 无法直接接触 background 的 WebSocket 私有变量,
11
+ // 这里只做命令级调试:通过一条 runtime 消息让 background 回包。
12
+ // status 文本仅反映扩展是否响应,不反映 WebSocket 真实连接态(那只在 background 内)。
13
+ async function pingBackground(): Promise<boolean> {
14
+ return new Promise((resolve) => {
15
+ chrome.runtime.sendMessage({ source: 'bee-plugin-popup', method: 'listTabs' }, (resp) => {
16
+ if (chrome.runtime.lastError) {
17
+ resolve(false)
18
+ return
19
+ }
20
+ resolve(!!resp)
21
+ })
22
+ })
23
+ }
24
+
25
+ document.getElementById('btn-tabs')!.addEventListener('click', async () => {
26
+ result.textContent = 'loading...'
27
+ chrome.runtime.sendMessage({ source: 'bee-plugin-popup', method: 'listTabs' }, (resp) => {
28
+ if (chrome.runtime.lastError) {
29
+ render(chrome.runtime.lastError.message)
30
+ return
31
+ }
32
+ render(resp)
33
+ })
34
+ })
35
+
36
+ document.getElementById('btn-status')!.addEventListener('click', async () => {
37
+ result.textContent = 'loading...'
38
+ chrome.runtime.sendMessage({ source: 'bee-plugin-popup', method: 'status' }, (resp) => {
39
+ if (chrome.runtime.lastError) {
40
+ render(chrome.runtime.lastError.message)
41
+ return
42
+ }
43
+ render(resp)
44
+ })
45
+ })
46
+
47
+ document.getElementById('btn-reconnect')!.addEventListener('click', () => {
48
+ // offscreen 持有 WS 并自重连;这里确保 offscreen document 存在
49
+ chrome.runtime.sendMessage({ source: 'bee-plugin-popup', method: 'reconnect' }, (resp) => {
50
+ if (chrome.runtime.lastError) {
51
+ render(chrome.runtime.lastError.message)
52
+ return
53
+ }
54
+ render(resp?.ok ? 'offscreen ensured; WS (re)connects automatically' : resp)
55
+ })
56
+ })
57
+
58
+ ;(async () => {
59
+ const ok = await pingBackground()
60
+ statusEl.textContent = ok ? 'background responsive' : 'background no response'
61
+ })()
@@ -0,0 +1,100 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ body {
6
+ margin: 0;
7
+ padding: 0;
8
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
9
+ font-size: 13px;
10
+ color: #333;
11
+ background: #f5f5f5;
12
+ }
13
+
14
+ #app {
15
+ width: 440px;
16
+ min-height: 300px;
17
+ padding: 16px;
18
+ }
19
+
20
+ header h1 {
21
+ margin: 0 0 4px;
22
+ font-size: 16px;
23
+ }
24
+
25
+ .status {
26
+ margin: 0 0 12px;
27
+ font-size: 12px;
28
+ color: #888;
29
+ }
30
+
31
+ section {
32
+ background: #fff;
33
+ border-radius: 8px;
34
+ padding: 12px;
35
+ margin-bottom: 12px;
36
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
37
+ }
38
+
39
+ section h2 {
40
+ font-size: 13px;
41
+ margin: 0 0 10px;
42
+ color: #666;
43
+ }
44
+
45
+ .row {
46
+ display: flex;
47
+ gap: 8px;
48
+ flex-wrap: wrap;
49
+ }
50
+
51
+ button {
52
+ padding: 6px 12px;
53
+ border: 1px solid #d9d9d9;
54
+ border-radius: 4px;
55
+ background: #fff;
56
+ cursor: pointer;
57
+ font-size: 12px;
58
+ }
59
+
60
+ button:hover {
61
+ border-color: #40a9ff;
62
+ color: #1890ff;
63
+ }
64
+
65
+ input[type='text'],
66
+ textarea {
67
+ width: 100%;
68
+ padding: 6px 8px;
69
+ margin-bottom: 8px;
70
+ border: 1px solid #d9d9d9;
71
+ border-radius: 4px;
72
+ font-family: monospace;
73
+ font-size: 12px;
74
+ }
75
+
76
+ textarea {
77
+ resize: vertical;
78
+ }
79
+
80
+ .inline {
81
+ display: flex;
82
+ align-items: center;
83
+ gap: 6px;
84
+ font-size: 12px;
85
+ color: #666;
86
+ margin-top: 6px;
87
+ }
88
+
89
+ #result {
90
+ background: #f6f8fa;
91
+ border: 1px solid #e1e4e8;
92
+ border-radius: 4px;
93
+ padding: 10px;
94
+ max-height: 280px;
95
+ overflow: auto;
96
+ font-size: 11px;
97
+ line-height: 1.5;
98
+ white-space: pre-wrap;
99
+ word-break: break-all;
100
+ }