@tom2012/cc-web 2026.5.24-b → 2026.5.24-c
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/README.md +1 -1
- package/backend/dist/__tests__/browser-chrome-e2e.test.d.ts +2 -0
- package/backend/dist/__tests__/browser-chrome-e2e.test.d.ts.map +1 -0
- package/backend/dist/__tests__/browser-chrome-e2e.test.js +121 -0
- package/backend/dist/__tests__/browser-chrome-e2e.test.js.map +1 -0
- package/backend/dist/__tests__/browser-chrome.test.d.ts +2 -0
- package/backend/dist/__tests__/browser-chrome.test.d.ts.map +1 -0
- package/backend/dist/__tests__/browser-chrome.test.js +169 -0
- package/backend/dist/__tests__/browser-chrome.test.js.map +1 -0
- package/backend/dist/browser-chrome/input-forwarder.d.ts +32 -0
- package/backend/dist/browser-chrome/input-forwarder.d.ts.map +1 -0
- package/backend/dist/browser-chrome/input-forwarder.js +92 -0
- package/backend/dist/browser-chrome/input-forwarder.js.map +1 -0
- package/backend/dist/browser-chrome/screencast.d.ts +21 -0
- package/backend/dist/browser-chrome/screencast.d.ts.map +1 -0
- package/backend/dist/browser-chrome/screencast.js +78 -0
- package/backend/dist/browser-chrome/screencast.js.map +1 -0
- package/backend/dist/browser-chrome/session-manager.d.ts +38 -0
- package/backend/dist/browser-chrome/session-manager.d.ts.map +1 -0
- package/backend/dist/browser-chrome/session-manager.js +175 -0
- package/backend/dist/browser-chrome/session-manager.js.map +1 -0
- package/backend/dist/index.d.ts.map +1 -1
- package/backend/dist/index.js +53 -0
- package/backend/dist/index.js.map +1 -1
- package/backend/dist/routes/browser-chrome.d.ts +4 -0
- package/backend/dist/routes/browser-chrome.d.ts.map +1 -0
- package/backend/dist/routes/browser-chrome.js +106 -0
- package/backend/dist/routes/browser-chrome.js.map +1 -0
- package/backend/package-lock.json +45 -0
- package/backend/package.json +1 -0
- package/frontend/dist/assets/{ChatOverlay-Cm7M9uTo.js → ChatOverlay-DWnJouqf.js} +1 -1
- package/frontend/dist/assets/{GraphPreview-Bu-zdWO0.js → GraphPreview-BhYiu0BC.js} +1 -1
- package/frontend/dist/assets/{MobilePage-DHSd8DBY.js → MobilePage-CpwaYT93.js} +3 -3
- package/frontend/dist/assets/{OfficePreview-CKOQ-aQd.js → OfficePreview-AIsCrFJN.js} +2 -2
- package/frontend/dist/assets/{PdfPreview-G8fpvo6_.js → PdfPreview-BOf3iH2G.js} +1 -1
- package/frontend/dist/assets/{ProjectPage-D8O-uvJ3.js → ProjectPage-BLDngPUN.js} +5 -5
- package/frontend/dist/assets/SettingsPage-898duvMO.js +13 -0
- package/frontend/dist/assets/{SkillHubPage-BcohQzQq.js → SkillHubPage-Cf3oFMk3.js} +1 -1
- package/frontend/dist/assets/{chevron-down-BjD1KDpf.js → chevron-down-CWdHpHQs.js} +1 -1
- package/frontend/dist/assets/{index-dlR56s5t.js → index-DoKF15jh.js} +1 -1
- package/frontend/dist/assets/{index-Fi22OrSt.js → index-DtlKk29C.js} +2 -2
- package/frontend/dist/assets/{index-BxSzFzfM.js → index-DzV3STk-.js} +1 -1
- package/frontend/dist/assets/{jszip.min-j8-Zud6N.js → jszip.min-BiyTb6vp.js} +1 -1
- package/frontend/dist/assets/{search-CfhpkpXs.js → search-DAr7qjB5.js} +1 -1
- package/frontend/dist/assets/select-IaWIRuom.js +13 -0
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
- package/frontend/dist/assets/SettingsPage-a3v0GKkG.js +0 -13
- package/frontend/dist/assets/select-nwQBXqLw.js +0 -13
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A self-hosted web application (distributed as npm package) that provides a browser-based interface for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI sessions. Create projects, each with a persistent terminal running Claude Code, and interact with them through a real-time terminal UI.
|
|
4
4
|
|
|
5
|
-
**Current version**: v2026.5.24-
|
|
5
|
+
**Current version**: v2026.5.24-c | [GitHub](https://github.com/zbc0315/cc-web) | MIT License
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-chrome-e2e.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/browser-chrome-e2e.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
/**
|
|
37
|
+
* E2E: launches real headless chromium via SessionManager and verifies
|
|
38
|
+
* screencast frames are delivered. Skipped if chromium binary missing.
|
|
39
|
+
*
|
|
40
|
+
* Run: npx vitest run src/__tests__/browser-chrome-e2e.test.ts
|
|
41
|
+
*/
|
|
42
|
+
const vitest_1 = require("vitest");
|
|
43
|
+
const http = __importStar(require("http"));
|
|
44
|
+
const playwright_1 = require("playwright");
|
|
45
|
+
const session_manager_1 = require("../browser-chrome/session-manager");
|
|
46
|
+
const screencast_1 = require("../browser-chrome/screencast");
|
|
47
|
+
// Skip if chromium binary not installed locally — keeps CI green when
|
|
48
|
+
// `npx playwright install chromium` hasn't run.
|
|
49
|
+
let chromiumAvailable = false;
|
|
50
|
+
try {
|
|
51
|
+
const path = playwright_1.chromium.executablePath();
|
|
52
|
+
chromiumAvailable = !!path;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
chromiumAvailable = false;
|
|
56
|
+
}
|
|
57
|
+
vitest_1.describe.skipIf(!chromiumAvailable)('browser-chrome e2e (real chromium)', () => {
|
|
58
|
+
let upstream;
|
|
59
|
+
let upstreamPort = 0;
|
|
60
|
+
// Minimal mock WS that captures sent messages — avoids needing a real
|
|
61
|
+
// server/client pair just to verify screencast delivers frames.
|
|
62
|
+
function mockWs() {
|
|
63
|
+
const sent = [];
|
|
64
|
+
const ws = {
|
|
65
|
+
readyState: 1, // OPEN
|
|
66
|
+
bufferedAmount: 0,
|
|
67
|
+
send: (data) => sent.push(data),
|
|
68
|
+
close: () => { ws.readyState = 3; },
|
|
69
|
+
};
|
|
70
|
+
return { sent, ws };
|
|
71
|
+
}
|
|
72
|
+
(0, vitest_1.afterAll)(async () => {
|
|
73
|
+
await session_manager_1.browserChromeSessions.destroyAll();
|
|
74
|
+
if (upstream)
|
|
75
|
+
await new Promise(r => upstream.close(() => r()));
|
|
76
|
+
}, 30000);
|
|
77
|
+
(0, vitest_1.it)('starts chromium, navigates to local server, and delivers screencast frames', async () => {
|
|
78
|
+
upstream = http.createServer((req, res) => {
|
|
79
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
80
|
+
// Animated page is required — CDP screencast only emits frames on
|
|
81
|
+
// visual change, a fully static page yields zero frames.
|
|
82
|
+
res.end(`<html><body style="background:#f00;font-size:48px">
|
|
83
|
+
<div id="c">0</div>
|
|
84
|
+
<script>let n=0;setInterval(()=>{document.getElementById('c').textContent=++n},50)</script>
|
|
85
|
+
</body></html>`);
|
|
86
|
+
});
|
|
87
|
+
await new Promise(r => upstream.listen(0, '127.0.0.1', r));
|
|
88
|
+
upstreamPort = upstream.address().port;
|
|
89
|
+
const session = await session_manager_1.browserChromeSessions.getOrCreate('e2e-user');
|
|
90
|
+
(0, vitest_1.expect)(session.sid).toBeTruthy();
|
|
91
|
+
(0, vitest_1.expect)(session.username).toBe('e2e-user');
|
|
92
|
+
await session.page.goto(`http://127.0.0.1:${upstreamPort}/`, { waitUntil: 'load' });
|
|
93
|
+
(0, vitest_1.expect)(session.page.url()).toContain('127.0.0.1');
|
|
94
|
+
const { sent, ws } = mockWs();
|
|
95
|
+
const stop = await (0, screencast_1.startScreencast)(session, ws);
|
|
96
|
+
// Wait for at least 1 frame (chromium usually emits within 200ms after page paints).
|
|
97
|
+
await new Promise((resolve, reject) => {
|
|
98
|
+
const t = setTimeout(() => reject(new Error('no frame in 3s')), 3000);
|
|
99
|
+
const i = setInterval(() => {
|
|
100
|
+
if (sent.length > 0) {
|
|
101
|
+
clearTimeout(t);
|
|
102
|
+
clearInterval(i);
|
|
103
|
+
resolve();
|
|
104
|
+
}
|
|
105
|
+
}, 50);
|
|
106
|
+
});
|
|
107
|
+
(0, vitest_1.expect)(sent.length).toBeGreaterThan(0);
|
|
108
|
+
const msg = JSON.parse(sent[0]);
|
|
109
|
+
(0, vitest_1.expect)(msg.type).toBe('frame');
|
|
110
|
+
(0, vitest_1.expect)(msg.format).toBe('jpeg');
|
|
111
|
+
(0, vitest_1.expect)(typeof msg.data).toBe('string');
|
|
112
|
+
(0, vitest_1.expect)(msg.data.length).toBeGreaterThan(100);
|
|
113
|
+
await stop();
|
|
114
|
+
}, 30000);
|
|
115
|
+
(0, vitest_1.it)('reuses session for same user', async () => {
|
|
116
|
+
const a = await session_manager_1.browserChromeSessions.getOrCreate('reuse-user');
|
|
117
|
+
const b = await session_manager_1.browserChromeSessions.getOrCreate('reuse-user');
|
|
118
|
+
(0, vitest_1.expect)(a.sid).toBe(b.sid);
|
|
119
|
+
}, 30000);
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=browser-chrome-e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-chrome-e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/browser-chrome-e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-chrome.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/browser-chrome.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const vitest_1 = require("vitest");
|
|
37
|
+
const jwt = __importStar(require("jsonwebtoken"));
|
|
38
|
+
const session_manager_1 = require("../browser-chrome/session-manager");
|
|
39
|
+
const config_1 = require("../config");
|
|
40
|
+
(0, vitest_1.describe)('mintSessionToken / verifySessionToken', () => {
|
|
41
|
+
(0, vitest_1.it)('round-trips a valid token', () => {
|
|
42
|
+
const token = (0, session_manager_1.mintSessionToken)('sid-1', 'tom');
|
|
43
|
+
const claim = (0, session_manager_1.verifySessionToken)(token);
|
|
44
|
+
(0, vitest_1.expect)(claim).toEqual({ sid: 'sid-1', username: 'tom' });
|
|
45
|
+
});
|
|
46
|
+
(0, vitest_1.it)('rejects token with wrong typ', () => {
|
|
47
|
+
const config = (0, config_1.getConfig)();
|
|
48
|
+
const wrongTyp = jwt.sign({ sid: 'x', username: 'tom', typ: 'user' }, config.jwtSecret, { expiresIn: '1h' });
|
|
49
|
+
(0, vitest_1.expect)((0, session_manager_1.verifySessionToken)(wrongTyp)).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
(0, vitest_1.it)('rejects token signed with wrong secret', () => {
|
|
52
|
+
const wrong = jwt.sign({ sid: 'x', username: 'tom', typ: 'browser-chrome' }, 'wrong-secret', { expiresIn: '1h' });
|
|
53
|
+
(0, vitest_1.expect)((0, session_manager_1.verifySessionToken)(wrong)).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
(0, vitest_1.it)('rejects token missing sid or username', () => {
|
|
56
|
+
const config = (0, config_1.getConfig)();
|
|
57
|
+
const noSid = jwt.sign({ username: 'tom', typ: 'browser-chrome' }, config.jwtSecret, { expiresIn: '1h' });
|
|
58
|
+
(0, vitest_1.expect)((0, session_manager_1.verifySessionToken)(noSid)).toBeNull();
|
|
59
|
+
const noUser = jwt.sign({ sid: 'x', typ: 'browser-chrome' }, config.jwtSecret, { expiresIn: '1h' });
|
|
60
|
+
(0, vitest_1.expect)((0, session_manager_1.verifySessionToken)(noUser)).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
(0, vitest_1.it)('rejects malformed token', () => {
|
|
63
|
+
(0, vitest_1.expect)((0, session_manager_1.verifySessionToken)('not-a-jwt')).toBeNull();
|
|
64
|
+
(0, vitest_1.expect)((0, session_manager_1.verifySessionToken)('')).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
// Pure-logic tests for input-forwarder require it to export sanitize helpers
|
|
68
|
+
// or accept a mock page. We test sanitize/clamp behavior indirectly by
|
|
69
|
+
// constructing edge-case messages and asserting they don't throw with a
|
|
70
|
+
// minimal mock Page.
|
|
71
|
+
(0, vitest_1.describe)('handleInput (with mock page)', () => {
|
|
72
|
+
// Lazy import to avoid pulling playwright at module load.
|
|
73
|
+
(0, vitest_1.it)('clamps out-of-range click coords and ignores invalid modifiers', async () => {
|
|
74
|
+
const calls = [];
|
|
75
|
+
const mockPage = {
|
|
76
|
+
mouse: {
|
|
77
|
+
click: async (x, y, opts) => { calls.push(['click', x, y, opts.button]); },
|
|
78
|
+
move: async () => { calls.push(['move']); },
|
|
79
|
+
wheel: async (dx, dy) => { calls.push(['wheel', dx, dy]); },
|
|
80
|
+
},
|
|
81
|
+
keyboard: {
|
|
82
|
+
down: async (k) => { calls.push(['kd', k]); },
|
|
83
|
+
up: async (k) => { calls.push(['ku', k]); },
|
|
84
|
+
press: async (k) => { calls.push(['kp', k]); },
|
|
85
|
+
type: async (t) => { calls.push(['type', t]); },
|
|
86
|
+
},
|
|
87
|
+
setViewportSize: async (s) => { calls.push(['vp', s.width, s.height]); },
|
|
88
|
+
};
|
|
89
|
+
const mockSession = {
|
|
90
|
+
sid: 's', username: 'u', browser: {}, context: {},
|
|
91
|
+
page: mockPage, cdp: {},
|
|
92
|
+
createdAt: 0, lastActivityAt: 0,
|
|
93
|
+
viewport: { w: 1280, h: 800 }, url: '',
|
|
94
|
+
};
|
|
95
|
+
const { handleInput } = await Promise.resolve().then(() => __importStar(require('../browser-chrome/input-forwarder')));
|
|
96
|
+
// Negative coords clamp to 0; over-max clamp to viewport.
|
|
97
|
+
await handleInput(mockSession, { type: 'click', x: -100, y: -50 });
|
|
98
|
+
(0, vitest_1.expect)(calls).toContainEqual(['click', 0, 0, 'left']);
|
|
99
|
+
calls.length = 0;
|
|
100
|
+
await handleInput(mockSession, { type: 'click', x: 9999, y: 9999, button: 'right', modifiers: ['Shift', 'EvilMod'] });
|
|
101
|
+
// EvilMod dropped; Shift held around click.
|
|
102
|
+
(0, vitest_1.expect)(calls).toEqual([
|
|
103
|
+
['kd', 'Shift'],
|
|
104
|
+
['click', 1280, 800, 'right'],
|
|
105
|
+
['ku', 'Shift'],
|
|
106
|
+
]);
|
|
107
|
+
// Scroll clamps delta.
|
|
108
|
+
calls.length = 0;
|
|
109
|
+
await handleInput(mockSession, { type: 'scroll', x: 100, y: 100, deltaX: 999999, deltaY: -999999 });
|
|
110
|
+
(0, vitest_1.expect)(calls).toEqual([
|
|
111
|
+
['move'],
|
|
112
|
+
['wheel', 10000, -10000],
|
|
113
|
+
]);
|
|
114
|
+
// type with too-long text rejected silently.
|
|
115
|
+
calls.length = 0;
|
|
116
|
+
await handleInput(mockSession, { type: 'type', text: 'x'.repeat(2000) });
|
|
117
|
+
(0, vitest_1.expect)(calls).toEqual([]);
|
|
118
|
+
// type with normal text works.
|
|
119
|
+
calls.length = 0;
|
|
120
|
+
await handleInput(mockSession, { type: 'type', text: 'hello' });
|
|
121
|
+
(0, vitest_1.expect)(calls).toEqual([['type', 'hello']]);
|
|
122
|
+
// resize clamps to bounds.
|
|
123
|
+
calls.length = 0;
|
|
124
|
+
await handleInput(mockSession, { type: 'resize', w: 50, h: 100000 });
|
|
125
|
+
(0, vitest_1.expect)(calls).toEqual([['vp', 200, 2160]]);
|
|
126
|
+
(0, vitest_1.expect)(mockSession.viewport).toEqual({ w: 200, h: 2160 });
|
|
127
|
+
});
|
|
128
|
+
(0, vitest_1.it)('key event uses press by default and releases modifiers in reverse', async () => {
|
|
129
|
+
const calls = [];
|
|
130
|
+
const mockPage = {
|
|
131
|
+
mouse: { click: async () => { }, move: async () => { }, wheel: async () => { } },
|
|
132
|
+
keyboard: {
|
|
133
|
+
down: async (k) => { calls.push(`d:${k}`); },
|
|
134
|
+
up: async (k) => { calls.push(`u:${k}`); },
|
|
135
|
+
press: async (k) => { calls.push(`p:${k}`); },
|
|
136
|
+
type: async () => { },
|
|
137
|
+
},
|
|
138
|
+
setViewportSize: async () => { },
|
|
139
|
+
};
|
|
140
|
+
const mockSession = {
|
|
141
|
+
sid: 's', username: 'u', browser: {}, context: {},
|
|
142
|
+
page: mockPage, cdp: {},
|
|
143
|
+
createdAt: 0, lastActivityAt: 0,
|
|
144
|
+
viewport: { w: 1280, h: 800 }, url: '',
|
|
145
|
+
};
|
|
146
|
+
const { handleInput } = await Promise.resolve().then(() => __importStar(require('../browser-chrome/input-forwarder')));
|
|
147
|
+
await handleInput(mockSession, { type: 'key', action: 'press', key: 'a', modifiers: ['Control', 'Shift'] });
|
|
148
|
+
(0, vitest_1.expect)(calls).toEqual(['d:Control', 'd:Shift', 'p:a', 'u:Shift', 'u:Control']);
|
|
149
|
+
});
|
|
150
|
+
(0, vitest_1.it)('drops key event when key is empty or too long', async () => {
|
|
151
|
+
const calls = [];
|
|
152
|
+
const mockPage = {
|
|
153
|
+
mouse: { click: async () => { }, move: async () => { }, wheel: async () => { } },
|
|
154
|
+
keyboard: { down: async () => { }, up: async () => { }, press: async (k) => { calls.push(k); }, type: async () => { } },
|
|
155
|
+
setViewportSize: async () => { },
|
|
156
|
+
};
|
|
157
|
+
const mockSession = {
|
|
158
|
+
sid: 's', username: 'u', browser: {}, context: {},
|
|
159
|
+
page: mockPage, cdp: {},
|
|
160
|
+
createdAt: 0, lastActivityAt: 0,
|
|
161
|
+
viewport: { w: 1280, h: 800 }, url: '',
|
|
162
|
+
};
|
|
163
|
+
const { handleInput } = await Promise.resolve().then(() => __importStar(require('../browser-chrome/input-forwarder')));
|
|
164
|
+
await handleInput(mockSession, { type: 'key', action: 'press', key: '' });
|
|
165
|
+
await handleInput(mockSession, { type: 'key', action: 'press', key: 'x'.repeat(100) });
|
|
166
|
+
(0, vitest_1.expect)(calls).toEqual([]);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=browser-chrome.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-chrome.test.js","sourceRoot":"","sources":["../../src/__tests__/browser-chrome.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,mCAA8C;AAC9C,kDAAoC;AACpC,uEAAyF;AACzF,sCAAsC;AAEtC,IAAA,iBAAQ,EAAC,uCAAuC,EAAE,GAAG,EAAE;IACrD,IAAA,WAAE,EAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,KAAK,GAAG,IAAA,kCAAgB,EAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAA,oCAAkB,EAAC,KAAK,CAAC,CAAC;QACxC,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,MAAM,GAAG,IAAA,kBAAS,GAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7G,IAAA,eAAM,EAAC,IAAA,oCAAkB,EAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,gBAAgB,EAAE,EAAE,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClH,IAAA,eAAM,EAAC,IAAA,oCAAkB,EAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,MAAM,GAAG,IAAA,kBAAS,GAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,gBAAgB,EAAE,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1G,IAAA,eAAM,EAAC,IAAA,oCAAkB,EAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,EAAE,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpG,IAAA,eAAM,EAAC,IAAA,oCAAkB,EAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,IAAA,eAAM,EAAC,IAAA,oCAAkB,EAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACnD,IAAA,eAAM,EAAC,IAAA,oCAAkB,EAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,6EAA6E;AAC7E,uEAAuE;AACvE,wEAAwE;AACxE,qBAAqB;AAErB,IAAA,iBAAQ,EAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,0DAA0D;IAC1D,IAAA,WAAE,EAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,KAAK,GAAkC,EAAE,CAAC;QAChD,MAAM,QAAQ,GAAG;YACf,KAAK,EAAE;gBACL,KAAK,EAAE,KAAK,EAAE,CAAS,EAAE,CAAS,EAAE,IAAwB,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9G,IAAI,EAAE,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3C,KAAK,EAAE,KAAK,EAAE,EAAU,EAAE,EAAU,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;aAC5E;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACrD,EAAE,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACnD,KAAK,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACtD,IAAI,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aACxD;YACD,eAAe,EAAE,KAAK,EAAE,CAAoC,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;SAC5G,CAAC;QACF,MAAM,WAAW,GAAG;YAClB,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,EAAW,EAAE,OAAO,EAAE,EAAW;YACnE,IAAI,EAAE,QAAiB,EAAE,GAAG,EAAE,EAAW;YACzC,SAAS,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC;YAC/B,QAAQ,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE;SACvC,CAAC;QACF,MAAM,EAAE,WAAW,EAAE,GAAG,wDAAa,mCAAmC,GAAC,CAAC;QAE1E,0DAA0D;QAC1D,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAEtD,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACjB,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;QACtH,4CAA4C;QAC5C,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC;YACpB,CAAC,IAAI,EAAE,OAAO,CAAC;YACf,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC;YAC7B,CAAC,IAAI,EAAE,OAAO,CAAC;SAChB,CAAC,CAAC;QAEH,uBAAuB;QACvB,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACjB,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QACpG,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC;YACpB,CAAC,MAAM,CAAC;YACR,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC;SACzB,CAAC,CAAC;QAEH,6CAA6C;QAC7C,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACjB,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzE,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAE1B,+BAA+B;QAC/B,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACjB,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAChE,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QAE3C,2BAA2B;QAC3B,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACjB,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACrE,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3C,IAAA,eAAM,EAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG;YACf,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE;YAC7E,QAAQ,EAAE;gBACR,IAAI,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBACpD,EAAE,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAClD,KAAK,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBACrD,IAAI,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;aACrB;YACD,eAAe,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;SAChC,CAAC;QACF,MAAM,WAAW,GAAG;YAClB,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,EAAW,EAAE,OAAO,EAAE,EAAW;YACnE,IAAI,EAAE,QAAiB,EAAE,GAAG,EAAE,EAAW;YACzC,SAAS,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC;YAC/B,QAAQ,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE;SACvC,CAAC;QACF,MAAM,EAAE,WAAW,EAAE,GAAG,wDAAa,mCAAmC,GAAC,CAAC;QAE1E,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5G,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG;YACf,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE;YAC7E,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,EAAE,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE;YAC5H,eAAe,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;SAChC,CAAC;QACF,MAAM,WAAW,GAAG;YAClB,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,EAAW,EAAE,OAAO,EAAE,EAAW;YACnE,IAAI,EAAE,QAAiB,EAAE,GAAG,EAAE,EAAW;YACzC,SAAS,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC;YAC/B,QAAQ,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE;SACvC,CAAC;QACF,MAAM,EAAE,WAAW,EAAE,GAAG,wDAAa,mCAAmC,GAAC,CAAC;QAE1E,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QAC1E,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvF,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Session } from './session-manager';
|
|
2
|
+
export type InputMsg = {
|
|
3
|
+
type: 'click';
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
button?: string;
|
|
7
|
+
modifiers?: string[];
|
|
8
|
+
} | {
|
|
9
|
+
type: 'mousemove';
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'scroll';
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
deltaX: number;
|
|
17
|
+
deltaY: number;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'key';
|
|
20
|
+
action: 'down' | 'up' | 'press';
|
|
21
|
+
key: string;
|
|
22
|
+
modifiers?: string[];
|
|
23
|
+
} | {
|
|
24
|
+
type: 'type';
|
|
25
|
+
text: string;
|
|
26
|
+
} | {
|
|
27
|
+
type: 'resize';
|
|
28
|
+
w: number;
|
|
29
|
+
h: number;
|
|
30
|
+
};
|
|
31
|
+
export declare function handleInput(session: Session, msg: InputMsg): Promise<void>;
|
|
32
|
+
//# sourceMappingURL=input-forwarder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-forwarder.d.ts","sourceRoot":"","sources":["../../src/browser-chrome/input-forwarder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAajD,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxE;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnF;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAgB7C,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DhF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleInput = handleInput;
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const log = (0, logger_1.modLogger)('browser-chrome:input');
|
|
6
|
+
// Whitelist of modifier names accepted from the client. Anything else is
|
|
7
|
+
// silently dropped — defends against bogus values being passed straight
|
|
8
|
+
// to playwright's keyboard API.
|
|
9
|
+
const VALID_MODIFIERS = ['Shift', 'Control', 'Alt', 'Meta'];
|
|
10
|
+
const VALID_BUTTONS = ['left', 'right', 'middle'];
|
|
11
|
+
function clamp(n, min, max) {
|
|
12
|
+
if (!Number.isFinite(n))
|
|
13
|
+
return min;
|
|
14
|
+
return Math.max(min, Math.min(max, Math.round(n)));
|
|
15
|
+
}
|
|
16
|
+
function sanitizeModifiers(arr) {
|
|
17
|
+
if (!Array.isArray(arr))
|
|
18
|
+
return [];
|
|
19
|
+
return arr.filter((m) => VALID_MODIFIERS.includes(m));
|
|
20
|
+
}
|
|
21
|
+
function sanitizeButton(b) {
|
|
22
|
+
return VALID_BUTTONS.includes(b ?? '') ? b : 'left';
|
|
23
|
+
}
|
|
24
|
+
async function handleInput(session, msg) {
|
|
25
|
+
session.lastActivityAt = Date.now();
|
|
26
|
+
switch (msg.type) {
|
|
27
|
+
case 'click': {
|
|
28
|
+
const x = clamp(msg.x, 0, session.viewport.w);
|
|
29
|
+
const y = clamp(msg.y, 0, session.viewport.h);
|
|
30
|
+
const mods = sanitizeModifiers(msg.modifiers);
|
|
31
|
+
for (const m of mods)
|
|
32
|
+
await session.page.keyboard.down(m);
|
|
33
|
+
try {
|
|
34
|
+
await session.page.mouse.click(x, y, { button: sanitizeButton(msg.button) });
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
for (const m of [...mods].reverse())
|
|
38
|
+
await session.page.keyboard.up(m).catch(() => { });
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case 'mousemove': {
|
|
43
|
+
const x = clamp(msg.x, 0, session.viewport.w);
|
|
44
|
+
const y = clamp(msg.y, 0, session.viewport.h);
|
|
45
|
+
await session.page.mouse.move(x, y);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
case 'scroll': {
|
|
49
|
+
const x = clamp(msg.x, 0, session.viewport.w);
|
|
50
|
+
const y = clamp(msg.y, 0, session.viewport.h);
|
|
51
|
+
await session.page.mouse.move(x, y);
|
|
52
|
+
await session.page.mouse.wheel(clamp(msg.deltaX, -10000, 10000), clamp(msg.deltaY, -10000, 10000));
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
case 'key': {
|
|
56
|
+
if (typeof msg.key !== 'string' || msg.key.length === 0 || msg.key.length > 32)
|
|
57
|
+
return;
|
|
58
|
+
const mods = sanitizeModifiers(msg.modifiers);
|
|
59
|
+
// Hold modifiers, perform key action, release modifiers (in reverse).
|
|
60
|
+
for (const m of mods)
|
|
61
|
+
await session.page.keyboard.down(m);
|
|
62
|
+
try {
|
|
63
|
+
if (msg.action === 'down')
|
|
64
|
+
await session.page.keyboard.down(msg.key);
|
|
65
|
+
else if (msg.action === 'up')
|
|
66
|
+
await session.page.keyboard.up(msg.key);
|
|
67
|
+
else
|
|
68
|
+
await session.page.keyboard.press(msg.key);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
for (const m of [...mods].reverse())
|
|
72
|
+
await session.page.keyboard.up(m).catch(() => { });
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case 'type': {
|
|
77
|
+
if (typeof msg.text !== 'string' || msg.text.length === 0 || msg.text.length > 1000)
|
|
78
|
+
return;
|
|
79
|
+
await session.page.keyboard.type(msg.text);
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case 'resize': {
|
|
83
|
+
const w = clamp(msg.w, 200, 3840);
|
|
84
|
+
const h = clamp(msg.h, 200, 2160);
|
|
85
|
+
await session.page.setViewportSize({ width: w, height: h });
|
|
86
|
+
session.viewport = { w, h };
|
|
87
|
+
log.info({ sid: session.sid, w, h }, 'viewport resized');
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=input-forwarder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-forwarder.js","sourceRoot":"","sources":["../../src/browser-chrome/input-forwarder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type WebSocket from 'ws';
|
|
2
|
+
import type { Session } from './session-manager';
|
|
3
|
+
export interface ScreencastConfig {
|
|
4
|
+
format: 'jpeg' | 'png';
|
|
5
|
+
quality: number;
|
|
6
|
+
maxWidth: number;
|
|
7
|
+
maxHeight: number;
|
|
8
|
+
everyNthFrame: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Start CDP screencast on the given session, forwarding base64 frames to the
|
|
12
|
+
* WebSocket as JSON messages. Returns a stopper that detaches the listener
|
|
13
|
+
* and stops the upstream screencast.
|
|
14
|
+
*
|
|
15
|
+
* Backpressure: if ws.bufferedAmount exceeds the ceiling we skip sending the
|
|
16
|
+
* frame to the client BUT still ack to chromium so it keeps producing.
|
|
17
|
+
* Dropping the frame is preferable to letting daemon memory balloon when
|
|
18
|
+
* a slow client falls behind.
|
|
19
|
+
*/
|
|
20
|
+
export declare function startScreencast(session: Session, ws: WebSocket, cfg?: Partial<ScreencastConfig>): Promise<() => Promise<void>>;
|
|
21
|
+
//# sourceMappingURL=screencast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screencast.d.ts","sourceRoot":"","sources":["../../src/browser-chrome/screencast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAChC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKjD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAmBD;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,SAAS,EACb,GAAG,GAAE,OAAO,CAAC,gBAAgB,CAAM,GAClC,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAqD9B"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.startScreencast = startScreencast;
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const log = (0, logger_1.modLogger)('browser-chrome:screencast');
|
|
6
|
+
const DEFAULT = {
|
|
7
|
+
format: 'jpeg',
|
|
8
|
+
quality: 70,
|
|
9
|
+
maxWidth: 1280,
|
|
10
|
+
maxHeight: 800,
|
|
11
|
+
// 30fps source / 3 → ~10fps. Research/PoC confirm sweet spot.
|
|
12
|
+
everyNthFrame: 3,
|
|
13
|
+
};
|
|
14
|
+
const WS_BUFFER_CEILING_BYTES = 4 * 1024 * 1024;
|
|
15
|
+
/**
|
|
16
|
+
* Start CDP screencast on the given session, forwarding base64 frames to the
|
|
17
|
+
* WebSocket as JSON messages. Returns a stopper that detaches the listener
|
|
18
|
+
* and stops the upstream screencast.
|
|
19
|
+
*
|
|
20
|
+
* Backpressure: if ws.bufferedAmount exceeds the ceiling we skip sending the
|
|
21
|
+
* frame to the client BUT still ack to chromium so it keeps producing.
|
|
22
|
+
* Dropping the frame is preferable to letting daemon memory balloon when
|
|
23
|
+
* a slow client falls behind.
|
|
24
|
+
*/
|
|
25
|
+
async function startScreencast(session, ws, cfg = {}) {
|
|
26
|
+
const config = { ...DEFAULT, ...cfg };
|
|
27
|
+
const onFrame = async (params) => {
|
|
28
|
+
if (ws.readyState !== 1 /* OPEN */)
|
|
29
|
+
return;
|
|
30
|
+
const dropForBackpressure = ws.bufferedAmount > WS_BUFFER_CEILING_BYTES;
|
|
31
|
+
if (!dropForBackpressure) {
|
|
32
|
+
try {
|
|
33
|
+
ws.send(JSON.stringify({
|
|
34
|
+
type: 'frame',
|
|
35
|
+
data: params.data,
|
|
36
|
+
format: config.format,
|
|
37
|
+
ts: Date.now(),
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
log.warn({ err, sid: session.sid }, 'frame send failed');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
log.warn({ sid: session.sid, buf: ws.bufferedAmount }, 'WS backlog, dropping frame');
|
|
46
|
+
}
|
|
47
|
+
// Always ack upstream — not acking would stall chromium for ALL clients
|
|
48
|
+
// sharing the page (we're single-client per page, but still hygienic).
|
|
49
|
+
try {
|
|
50
|
+
await session.cdp.send('Page.screencastFrameAck', { sessionId: params.sessionId });
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
// CDP may already be detached during teardown — not an error.
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
session.cdp.on('Page.screencastFrame', onFrame);
|
|
57
|
+
await session.cdp.send('Page.startScreencast', {
|
|
58
|
+
format: config.format,
|
|
59
|
+
quality: config.quality,
|
|
60
|
+
maxWidth: config.maxWidth,
|
|
61
|
+
maxHeight: config.maxHeight,
|
|
62
|
+
everyNthFrame: config.everyNthFrame,
|
|
63
|
+
});
|
|
64
|
+
log.info({ sid: session.sid, config }, 'screencast started');
|
|
65
|
+
let stopped = false;
|
|
66
|
+
return async () => {
|
|
67
|
+
if (stopped)
|
|
68
|
+
return;
|
|
69
|
+
stopped = true;
|
|
70
|
+
session.cdp.off('Page.screencastFrame', onFrame);
|
|
71
|
+
try {
|
|
72
|
+
await session.cdp.send('Page.stopScreencast');
|
|
73
|
+
}
|
|
74
|
+
catch { /* session may already be dead */ }
|
|
75
|
+
log.info({ sid: session.sid }, 'screencast stopped');
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=screencast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screencast.js","sourceRoot":"","sources":["../../src/browser-chrome/screencast.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type Browser, type BrowserContext, type Page, type CDPSession } from 'playwright';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
export interface Session {
|
|
4
|
+
sid: string;
|
|
5
|
+
username: string;
|
|
6
|
+
browser: Browser;
|
|
7
|
+
context: BrowserContext;
|
|
8
|
+
page: Page;
|
|
9
|
+
cdp: CDPSession;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
lastActivityAt: number;
|
|
12
|
+
viewport: {
|
|
13
|
+
w: number;
|
|
14
|
+
h: number;
|
|
15
|
+
};
|
|
16
|
+
url: string;
|
|
17
|
+
}
|
|
18
|
+
declare class SessionManager extends EventEmitter {
|
|
19
|
+
private sessions;
|
|
20
|
+
private idleTimer;
|
|
21
|
+
private started;
|
|
22
|
+
start(): void;
|
|
23
|
+
/** Return existing session for this user (reuse) or create a new one. */
|
|
24
|
+
getOrCreate(username: string): Promise<Session>;
|
|
25
|
+
get(sid: string, username: string): Session | null;
|
|
26
|
+
destroy(sid: string): Promise<void>;
|
|
27
|
+
destroyAll(): Promise<void>;
|
|
28
|
+
size(): number;
|
|
29
|
+
private sweepIdle;
|
|
30
|
+
}
|
|
31
|
+
export declare const browserChromeSessions: SessionManager;
|
|
32
|
+
export declare function mintSessionToken(sid: string, username: string): string;
|
|
33
|
+
export declare function verifySessionToken(token: string): {
|
|
34
|
+
sid: string;
|
|
35
|
+
username: string;
|
|
36
|
+
} | null;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/browser-chrome/session-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAErG,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAOtC,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,UAAU,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,GAAG,EAAE,MAAM,CAAC;CACb;AAMD,cAAM,cAAe,SAAQ,YAAY;IACvC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,OAAO,CAAS;IAExB,KAAK,IAAI,IAAI;IAMb,yEAAyE;IACnE,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiDrD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAQ5C,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAanC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,IAAI,IAAI,MAAM;IAId,OAAO,CAAC,SAAS;CASlB;AAED,eAAO,MAAM,qBAAqB,gBAAuB,CAAC;AAO1D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOtE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAU1F"}
|