cssprobe-cli 0.2.5 → 0.2.7
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.md +83 -15
- package/README.md +124 -68
- package/dist/browser/launcher.d.ts +8 -0
- package/dist/browser/launcher.js +8 -2
- package/dist/cli/command.d.ts +1 -1
- package/dist/cli/commands.d.ts +6 -30
- package/dist/cli/commands.js +100 -47
- package/dist/cli/program.js +146 -388
- package/dist/config/config.d.ts +5 -0
- package/dist/config/config.js +14 -0
- package/dist/config/helpGenerator.js +3 -1
- package/dist/daemon/daemon.d.ts +11 -0
- package/dist/daemon/daemon.js +233 -0
- package/dist/daemon/daemonEntry.d.ts +6 -0
- package/dist/daemon/daemonEntry.js +27 -0
- package/dist/daemon/session.d.ts +52 -0
- package/dist/daemon/session.js +220 -0
- package/dist/daemonEntry.js +1440 -0
- package/dist/help.json +66 -32
- package/dist/utils/socketConnection.d.ts +21 -0
- package/dist/utils/socketConnection.js +77 -0
- package/docs/code-design-session-mode.md +393 -0
- package/docs/guide/installation.md +3 -2
- package/package.json +1 -1
package/dist/help.json
CHANGED
|
@@ -1,55 +1,90 @@
|
|
|
1
1
|
{
|
|
2
|
-
"global": "Usage: cssprobe-cli <command> [args] [options]\n\n\
|
|
2
|
+
"global": "Usage: cssprobe-cli <command> [args] [options]\n\n\nSession Management:\n open [url] open browser in session mode (non-blocking)\n close close the browser session\n status show current session status\n eval <expression> evaluate javascript expression on the page\n state-import [file] import cookies from netscape format file\n\nCSS Inspection:\n inspect <selector> inspect the runtime css of a page element\n tree <selector> show dom tree structure\n layout <selector> show ascii layout diagram\n findings <selector> show only issues/warnings/errors\n inject-css <css> inject css into the current page\n\nExport:\n screenshot take a screenshot of the current page\n\nConfiguration:\n config-show show current configuration\n config-set <key> <value> set a configuration value\n config-list list all profiles\n config-use <name> switch active profile\n config-new <name> create a new profile\n config-path show configuration file path\n\nAgent Skills:\n skill-install install agent skill for cssprobe-cli\n skill-uninstall remove agent skill for cssprobe-cli\n\nGlobal options:\n --help [command] print help\n --json output response as JSON\n --raw output only the result value\n --version print version\n -p, --profile <name> use named profile (or CSSPROBE_PROFILE env)",
|
|
3
3
|
"commands": {
|
|
4
|
+
"open": {
|
|
5
|
+
"help": "cssprobe-cli open [url]\n\nOpen browser in session mode (non-blocking)\n\nArguments:\n [url] url, file:// path, or local html file path\nOptions:\n --browser browser engine (default chromium)\n --headed show the browser window\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
6
|
+
"flags": {
|
|
7
|
+
"browser": "string",
|
|
8
|
+
"headed": "boolean"
|
|
9
|
+
},
|
|
10
|
+
"args": [
|
|
11
|
+
"url"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"close": {
|
|
15
|
+
"help": "cssprobe-cli close \n\nClose the browser session\n\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
16
|
+
"flags": {},
|
|
17
|
+
"args": []
|
|
18
|
+
},
|
|
19
|
+
"status": {
|
|
20
|
+
"help": "cssprobe-cli status \n\nShow current session status\n\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
21
|
+
"flags": {},
|
|
22
|
+
"args": []
|
|
23
|
+
},
|
|
4
24
|
"inspect": {
|
|
5
|
-
"help": "cssprobe-cli inspect <
|
|
25
|
+
"help": "cssprobe-cli inspect <selector>\n\nInspect the runtime CSS of a page element\n\nArguments:\n <selector> css selector for the root element\nOptions:\n --json output structured json instead of markdown\n --brief compact output: tree sketch + warnings/errors only\n --layout ascii layout diagram showing element positions and sizes\n --depth dom tree depth (default: auto, max 20)\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
6
26
|
"flags": {
|
|
7
27
|
"json": "boolean",
|
|
8
|
-
"headed": "boolean",
|
|
9
|
-
"browser": "string",
|
|
10
|
-
"zoom": "boolean",
|
|
11
|
-
"depth": "string",
|
|
12
|
-
"max-nodes": "string",
|
|
13
|
-
"up-to": "string",
|
|
14
|
-
"state": "string",
|
|
15
28
|
"brief": "boolean",
|
|
16
29
|
"layout": "boolean",
|
|
17
|
-
"
|
|
30
|
+
"depth": "string"
|
|
18
31
|
},
|
|
19
32
|
"args": [
|
|
20
|
-
"url",
|
|
21
33
|
"selector"
|
|
22
34
|
]
|
|
23
35
|
},
|
|
24
|
-
"
|
|
25
|
-
"help": "cssprobe-cli
|
|
36
|
+
"tree": {
|
|
37
|
+
"help": "cssprobe-cli tree <selector>\n\nShow DOM tree structure\n\nArguments:\n <selector> css selector for the root element\nOptions:\n --depth tree depth (default: auto, max 20)\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
26
38
|
"flags": {
|
|
27
|
-
"
|
|
28
|
-
"out": "string"
|
|
39
|
+
"depth": "string"
|
|
29
40
|
},
|
|
30
41
|
"args": [
|
|
31
|
-
"
|
|
42
|
+
"selector"
|
|
32
43
|
]
|
|
33
44
|
},
|
|
34
|
-
"
|
|
35
|
-
"help": "cssprobe-cli
|
|
45
|
+
"layout": {
|
|
46
|
+
"help": "cssprobe-cli layout <selector>\n\nShow ASCII layout diagram\n\nArguments:\n <selector> css selector for the root element\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
47
|
+
"flags": {},
|
|
48
|
+
"args": [
|
|
49
|
+
"selector"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"findings": {
|
|
53
|
+
"help": "cssprobe-cli findings <selector>\n\nShow only issues/warnings/errors\n\nArguments:\n <selector> css selector for the root element\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
54
|
+
"flags": {},
|
|
55
|
+
"args": [
|
|
56
|
+
"selector"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"inject-css": {
|
|
60
|
+
"help": "cssprobe-cli inject-css <css>\n\nInject CSS into the current page\n\nArguments:\n <css> css code to inject\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
61
|
+
"flags": {},
|
|
62
|
+
"args": [
|
|
63
|
+
"css"
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"screenshot": {
|
|
67
|
+
"help": "cssprobe-cli screenshot \n\nTake a screenshot of the current page\n\nOptions:\n --full-page take screenshot of the full scrollable page\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
36
68
|
"flags": {
|
|
37
|
-
"
|
|
38
|
-
"merge": "string"
|
|
69
|
+
"full-page": "boolean"
|
|
39
70
|
},
|
|
71
|
+
"args": []
|
|
72
|
+
},
|
|
73
|
+
"eval": {
|
|
74
|
+
"help": "cssprobe-cli eval <expression>\n\nEvaluate JavaScript expression on the page\n\nArguments:\n <expression> javascript expression to evaluate\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
75
|
+
"flags": {},
|
|
40
76
|
"args": [
|
|
41
|
-
"
|
|
77
|
+
"expression"
|
|
42
78
|
]
|
|
43
79
|
},
|
|
44
|
-
"
|
|
45
|
-
"help": "cssprobe-cli
|
|
80
|
+
"state-import": {
|
|
81
|
+
"help": "cssprobe-cli state-import [file]\n\nImport cookies from Netscape format file\n\nArguments:\n [file] netscape cookie file path (reads from stdin if omitted)\nOptions:\n --out output state file path (default: ~/.cssprobe-cli/states/imported.json)\n --merge existing state file to merge into\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
46
82
|
"flags": {
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"depth": "string"
|
|
83
|
+
"out": "string",
|
|
84
|
+
"merge": "string"
|
|
50
85
|
},
|
|
51
86
|
"args": [
|
|
52
|
-
"
|
|
87
|
+
"file"
|
|
53
88
|
]
|
|
54
89
|
},
|
|
55
90
|
"config-show": {
|
|
@@ -60,7 +95,7 @@
|
|
|
60
95
|
"args": []
|
|
61
96
|
},
|
|
62
97
|
"config-set": {
|
|
63
|
-
"help": "cssprobe-cli config-set <key> <value>\n\nSet a configuration value\n\nArguments:\n <key> config key (browser, depth, headed)\n <value> config value\nOptions:\n --profile profile name",
|
|
98
|
+
"help": "cssprobe-cli config-set <key> <value>\n\nSet a configuration value\n\nArguments:\n <key> config key (browser, depth, headed, viewport)\n <value> config value\nOptions:\n --profile profile name",
|
|
64
99
|
"flags": {
|
|
65
100
|
"profile": "string"
|
|
66
101
|
},
|
|
@@ -94,7 +129,7 @@
|
|
|
94
129
|
"args": []
|
|
95
130
|
},
|
|
96
131
|
"skill-install": {
|
|
97
|
-
"help": "cssprobe-cli skill-install \n\nInstall Agent Skill for cssprobe-cli
|
|
132
|
+
"help": "cssprobe-cli skill-install \n\nInstall Agent Skill for cssprobe-cli\n\nOptions:\n --target agent target: auto, all, opencode, claude, codex, cursor, hermes, gemini (default: auto)\n --local install to current directory instead of home\n --path custom base directory (overrides home/current)\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
98
133
|
"flags": {
|
|
99
134
|
"target": "string",
|
|
100
135
|
"local": "boolean",
|
|
@@ -113,12 +148,11 @@
|
|
|
113
148
|
}
|
|
114
149
|
},
|
|
115
150
|
"booleanOptions": [
|
|
116
|
-
"json",
|
|
117
151
|
"headed",
|
|
118
|
-
"
|
|
152
|
+
"json",
|
|
119
153
|
"brief",
|
|
120
154
|
"layout",
|
|
121
|
-
"
|
|
155
|
+
"full-page",
|
|
122
156
|
"local"
|
|
123
157
|
]
|
|
124
158
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Socket communication utility.
|
|
3
|
+
* Adapted from playwright-cli.
|
|
4
|
+
*/
|
|
5
|
+
import net from 'net';
|
|
6
|
+
export declare class SocketConnection {
|
|
7
|
+
private _socket;
|
|
8
|
+
private _pendingBuffers;
|
|
9
|
+
onclose?: () => void;
|
|
10
|
+
onmessage?: (message: any) => void;
|
|
11
|
+
constructor(socket: net.Socket);
|
|
12
|
+
send(message: {
|
|
13
|
+
id: number;
|
|
14
|
+
error?: string;
|
|
15
|
+
result?: any;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
close(): void;
|
|
18
|
+
private _onData;
|
|
19
|
+
private _dispatchMessage;
|
|
20
|
+
}
|
|
21
|
+
export declare function compareSemver(a: string, b: string): number;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Socket communication utility.
|
|
4
|
+
* Adapted from playwright-cli.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SocketConnection = void 0;
|
|
8
|
+
exports.compareSemver = compareSemver;
|
|
9
|
+
class SocketConnection {
|
|
10
|
+
_socket;
|
|
11
|
+
_pendingBuffers = [];
|
|
12
|
+
onclose;
|
|
13
|
+
onmessage;
|
|
14
|
+
constructor(socket) {
|
|
15
|
+
this._socket = socket;
|
|
16
|
+
socket.on('data', buffer => this._onData(buffer));
|
|
17
|
+
socket.on('close', () => {
|
|
18
|
+
this.onclose?.();
|
|
19
|
+
});
|
|
20
|
+
socket.on('error', e => console.error(`error: ${e.message}`));
|
|
21
|
+
}
|
|
22
|
+
async send(message) {
|
|
23
|
+
await new Promise((resolve, reject) => {
|
|
24
|
+
this._socket.write(`${JSON.stringify(message)}\n`, error => {
|
|
25
|
+
if (error)
|
|
26
|
+
reject(error);
|
|
27
|
+
else
|
|
28
|
+
resolve(undefined);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
close() {
|
|
33
|
+
this._socket.destroy();
|
|
34
|
+
}
|
|
35
|
+
_onData(buffer) {
|
|
36
|
+
let end = buffer.indexOf('\n');
|
|
37
|
+
if (end === -1) {
|
|
38
|
+
this._pendingBuffers.push(buffer);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this._pendingBuffers.push(buffer.slice(0, end));
|
|
42
|
+
const message = Buffer.concat(this._pendingBuffers).toString();
|
|
43
|
+
this._dispatchMessage(message);
|
|
44
|
+
let start = end + 1;
|
|
45
|
+
end = buffer.indexOf('\n', start);
|
|
46
|
+
while (end !== -1) {
|
|
47
|
+
const message = buffer.toString(undefined, start, end);
|
|
48
|
+
this._dispatchMessage(message);
|
|
49
|
+
start = end + 1;
|
|
50
|
+
end = buffer.indexOf('\n', start);
|
|
51
|
+
}
|
|
52
|
+
this._pendingBuffers = [buffer.slice(start)];
|
|
53
|
+
}
|
|
54
|
+
_dispatchMessage(message) {
|
|
55
|
+
try {
|
|
56
|
+
this.onmessage?.(JSON.parse(message));
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
console.error('failed to dispatch message', e);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.SocketConnection = SocketConnection;
|
|
64
|
+
function compareSemver(a, b) {
|
|
65
|
+
const aBase = a.replace(/-.*$/, '');
|
|
66
|
+
const bBase = b.replace(/-.*$/, '');
|
|
67
|
+
const aParts = aBase.split('.').map(Number);
|
|
68
|
+
const bParts = bBase.split('.').map(Number);
|
|
69
|
+
for (let i = 0; i < 3; i++) {
|
|
70
|
+
if (aParts[i] > bParts[i])
|
|
71
|
+
return 1;
|
|
72
|
+
if (aParts[i] < bParts[i])
|
|
73
|
+
return -1;
|
|
74
|
+
}
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=socketConnection.js.map
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
# Code Design: Session Mode
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
cssprobe-cli 将复用 playwright-cli 的 daemon + session 架构,提供非阻塞的浏览器管理和 CSS 诊断能力。
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
cssprobe-cli
|
|
11
|
+
├── src/
|
|
12
|
+
│ ├── cli/
|
|
13
|
+
│ │ ├── program.ts # CLI 入口
|
|
14
|
+
│ │ ├── commands.ts # 命令定义(合并 playwright-cli + cssprobe 命令)
|
|
15
|
+
│ │ ├── command.ts # 命令解析
|
|
16
|
+
│ │ ├── output.ts # 输出格式化
|
|
17
|
+
│ │ └── minimist.ts # 参数解析(已有)
|
|
18
|
+
│ ├── daemon/
|
|
19
|
+
│ │ ├── daemon.ts # Daemon 进程(复制自 playwright-cli)
|
|
20
|
+
│ │ ├── session.ts # Session 管理(简化版,单 session)
|
|
21
|
+
│ │ └── registry.ts # Session 注册表(简化版)
|
|
22
|
+
│ ├── backend/
|
|
23
|
+
│ │ ├── browserBackend.ts # 浏览器后端(复制自 playwright-cli)
|
|
24
|
+
│ │ ├── context.ts # 浏览器上下文(复制自 playwright-cli)
|
|
25
|
+
│ │ ├── tool.ts # Tool 接口(复制自 playwright-cli)
|
|
26
|
+
│ │ ├── tools.ts # Tool 注册(合并 playwright + cssprobe tools)
|
|
27
|
+
│ │ ├── response.ts # 响应处理(复制自 playwright-cli)
|
|
28
|
+
│ │ └── tools/
|
|
29
|
+
│ │ ├── evaluate.ts # JS 执行(复制自 playwright-cli)
|
|
30
|
+
│ │ ├── screenshot.ts # 截图(复制自 playwright-cli)
|
|
31
|
+
│ │ ├── pdf.ts # PDF(复制自 playwright-cli)
|
|
32
|
+
│ │ ├── navigate.ts # 导航(复制自 playwright-cli)
|
|
33
|
+
│ │ ├── tabs.ts # Tab 管理(复制自 playwright-cli)
|
|
34
|
+
│ │ ├── cssprobe.ts # CSSprobe 工具(新增)
|
|
35
|
+
│ │ │ ├── inspect.ts # CSS 诊断
|
|
36
|
+
│ │ │ ├── tree.ts # DOM 树
|
|
37
|
+
│ │ │ ├── layout.ts # 布局图
|
|
38
|
+
│ │ │ ├── findings.ts # 问题发现
|
|
39
|
+
│ │ │ ├── snapshot.ts # 快照
|
|
40
|
+
│ │ │ └── injectCss.ts# CSS 注入
|
|
41
|
+
│ │ └── index.ts # 工具导出
|
|
42
|
+
│ ├── browser/
|
|
43
|
+
│ │ └── launcher.ts # 浏览器启动(保留,用于 daemon 启动)
|
|
44
|
+
│ ├── engine/
|
|
45
|
+
│ │ ├── collector.ts # 数据收集(保留)
|
|
46
|
+
│ │ ├── analyzer.ts # 分析(保留)
|
|
47
|
+
│ │ └── renderer.ts # 渲染(保留)
|
|
48
|
+
│ └── utils/
|
|
49
|
+
│ └── socketConnection.ts # Socket 通信(复制自 playwright-cli)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Key Components
|
|
53
|
+
|
|
54
|
+
### 1. Session Management
|
|
55
|
+
|
|
56
|
+
**简化版单 Session 实现:**
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// src/daemon/session.ts
|
|
60
|
+
export class Session {
|
|
61
|
+
readonly name: string = 'default';
|
|
62
|
+
private _sessionFile: SessionFile;
|
|
63
|
+
|
|
64
|
+
async run(args: MinimistArgs): Promise<{ text: string }> {
|
|
65
|
+
const { socket } = await this._connect();
|
|
66
|
+
return await SocketConnectionClient.sendAndClose(socket, 'run', { args, cwd: process.cwd() });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async stop(): Promise<void> {
|
|
70
|
+
const { socket } = await this._connect();
|
|
71
|
+
await SocketConnectionClient.sendAndClose(socket, 'stop', {});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private async _connect(): Promise<{ socket?: net.Socket }> {
|
|
75
|
+
const sessionConfig = await this._loadSessionConfig();
|
|
76
|
+
if (!sessionConfig) return {};
|
|
77
|
+
return { socket: net.createConnection(sessionConfig.socketPath) };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Session 文件位置:**
|
|
83
|
+
- macOS: `~/Library/Caches/ms-playwright/daemon/<workspaceHash>/default.session`
|
|
84
|
+
- Linux: `~/.cache/ms-playwright/daemon/<workspaceHash>/default.session`
|
|
85
|
+
|
|
86
|
+
### 2. Daemon Process
|
|
87
|
+
|
|
88
|
+
**Daemon 启动流程:**
|
|
89
|
+
1. `cssprobe-cli open <url>` 调用 `Session.startDaemon()`
|
|
90
|
+
2. Daemon 进程启动,监听 Unix socket
|
|
91
|
+
3. 接收命令,通过 BrowserBackend 执行
|
|
92
|
+
4. 返回结果给客户端
|
|
93
|
+
|
|
94
|
+
**Daemon 入口:**
|
|
95
|
+
```typescript
|
|
96
|
+
// src/daemon/daemonEntry.ts
|
|
97
|
+
import { startCliDaemonServer } from './daemon';
|
|
98
|
+
import { BrowserBackend } from '../backend/browserBackend';
|
|
99
|
+
import { browserTools } from '../backend/tools';
|
|
100
|
+
|
|
101
|
+
export async function runDaemon(sessionName: string, args: string[]) {
|
|
102
|
+
// 1. 启动浏览器
|
|
103
|
+
const browser = await chromium.launch({ headless: false });
|
|
104
|
+
const context = await browser.newContext();
|
|
105
|
+
|
|
106
|
+
// 2. 创建 BrowserBackend
|
|
107
|
+
const backend = new BrowserBackend(config, context, browserTools);
|
|
108
|
+
|
|
109
|
+
// 3. 启动 daemon server
|
|
110
|
+
await startCliDaemonServer(sessionName, context, browserInfo, config, clientInfo, mcpClientInfo, options);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 3. Backend Tools
|
|
115
|
+
|
|
116
|
+
**现有 playwright-cli 工具(需要复制):**
|
|
117
|
+
- `evaluate.ts` - JS 执行
|
|
118
|
+
- `screenshot.ts` - 截图
|
|
119
|
+
- `pdf.ts` - PDF 生成
|
|
120
|
+
- `navigate.ts` - 页面导航
|
|
121
|
+
- `tabs.ts` - Tab 管理
|
|
122
|
+
|
|
123
|
+
**新增 cssprobe 工具:**
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// src/backend/tools/cssprobe/inspect.ts
|
|
127
|
+
import { defineTabTool } from '../tool';
|
|
128
|
+
import { collect } from '../../../engine/collector';
|
|
129
|
+
import { analyze } from '../../../engine/analyzer';
|
|
130
|
+
import { renderMarkdown, renderJSON } from '../../../engine/renderer';
|
|
131
|
+
|
|
132
|
+
const inspectTool = defineTabTool({
|
|
133
|
+
capability: 'core',
|
|
134
|
+
schema: {
|
|
135
|
+
name: 'cssprobe_inspect',
|
|
136
|
+
title: 'CSS Inspection',
|
|
137
|
+
description: 'Inspect runtime CSS of a page element',
|
|
138
|
+
inputSchema: z.object({
|
|
139
|
+
selector: z.string().describe('CSS selector for the root element'),
|
|
140
|
+
depth: z.number().optional().describe('DOM tree depth'),
|
|
141
|
+
json: z.boolean().optional().describe('Output as JSON'),
|
|
142
|
+
brief: z.boolean().optional().describe('Compact output'),
|
|
143
|
+
layout: z.boolean().optional().describe('ASCII layout diagram'),
|
|
144
|
+
}),
|
|
145
|
+
type: 'readOnly',
|
|
146
|
+
},
|
|
147
|
+
handle: async (tab, params, response) => {
|
|
148
|
+
const snapshot = await collect(tab.page, {
|
|
149
|
+
rootSelector: params.selector,
|
|
150
|
+
downDepth: params.depth || 6,
|
|
151
|
+
maxNodes: 60,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const findings = analyze(snapshot);
|
|
155
|
+
|
|
156
|
+
if (params.json) {
|
|
157
|
+
response.addResult('Inspection result', JSON.stringify(renderJSON(snapshot, findings, params.brief), null, 2));
|
|
158
|
+
} else {
|
|
159
|
+
response.addResult('Inspection result', renderMarkdown(snapshot, findings, params.brief, params.layout));
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 4. Command Definitions
|
|
166
|
+
|
|
167
|
+
**合并 playwright-cli 和 cssprobe 命令:**
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// src/cli/commands.ts
|
|
171
|
+
import * as z from 'zod';
|
|
172
|
+
import { declareCommand } from './command';
|
|
173
|
+
|
|
174
|
+
// 浏览器管理命令(复用 playwright-cli)
|
|
175
|
+
const open = declareCommand({
|
|
176
|
+
name: 'open',
|
|
177
|
+
category: 'core',
|
|
178
|
+
description: 'Open the browser',
|
|
179
|
+
args: z.object({
|
|
180
|
+
url: z.string().optional().describe('URL to navigate to'),
|
|
181
|
+
}),
|
|
182
|
+
options: z.object({
|
|
183
|
+
browser: z.string().optional().describe('Browser engine'),
|
|
184
|
+
headed: z.boolean().optional().describe('Show browser window'),
|
|
185
|
+
}),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const close = declareCommand({
|
|
189
|
+
name: 'close',
|
|
190
|
+
category: 'core',
|
|
191
|
+
description: 'Close the browser',
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// CSSprobe 增强命令(新增)
|
|
195
|
+
const inspect = declareCommand({
|
|
196
|
+
name: 'inspect',
|
|
197
|
+
category: 'cssprobe',
|
|
198
|
+
description: 'Inspect runtime CSS of a page element',
|
|
199
|
+
args: z.object({
|
|
200
|
+
selector: z.string().describe('CSS selector for the root element'),
|
|
201
|
+
}),
|
|
202
|
+
options: z.object({
|
|
203
|
+
json: z.boolean().optional().describe('Output as JSON'),
|
|
204
|
+
brief: z.boolean().optional().describe('Compact output'),
|
|
205
|
+
layout: z.boolean().optional().describe('ASCII layout diagram'),
|
|
206
|
+
depth: z.number().optional().describe('DOM tree depth'),
|
|
207
|
+
}),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const tree = declareCommand({
|
|
211
|
+
name: 'tree',
|
|
212
|
+
category: 'cssprobe',
|
|
213
|
+
description: 'Show DOM tree structure',
|
|
214
|
+
args: z.object({
|
|
215
|
+
selector: z.string().describe('CSS selector for the root element'),
|
|
216
|
+
}),
|
|
217
|
+
options: z.object({
|
|
218
|
+
depth: z.number().optional().describe('Tree depth'),
|
|
219
|
+
}),
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const layout = declareCommand({
|
|
223
|
+
name: 'layout',
|
|
224
|
+
category: 'cssprobe',
|
|
225
|
+
description: 'Show ASCII layout diagram',
|
|
226
|
+
args: z.object({
|
|
227
|
+
selector: z.string().describe('CSS selector for the root element'),
|
|
228
|
+
}),
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const findings = declareCommand({
|
|
232
|
+
name: 'findings',
|
|
233
|
+
category: 'cssprobe',
|
|
234
|
+
description: 'Show only issues/warnings',
|
|
235
|
+
args: z.object({
|
|
236
|
+
selector: z.string().describe('CSS selector for the root element'),
|
|
237
|
+
}),
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const injectCss = declareCommand({
|
|
241
|
+
name: 'inject-css',
|
|
242
|
+
category: 'cssprobe',
|
|
243
|
+
description: 'Inject CSS into the current page',
|
|
244
|
+
args: z.object({
|
|
245
|
+
css: z.string().describe('CSS code to inject'),
|
|
246
|
+
}),
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// 导出所有命令
|
|
250
|
+
export const commands = {
|
|
251
|
+
open,
|
|
252
|
+
close,
|
|
253
|
+
inspect,
|
|
254
|
+
tree,
|
|
255
|
+
layout,
|
|
256
|
+
findings,
|
|
257
|
+
'inject-css': injectCss,
|
|
258
|
+
// ... 其他命令
|
|
259
|
+
};
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### 5. Socket Communication
|
|
263
|
+
|
|
264
|
+
**复制自 playwright-cli,无需修改:**
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// src/utils/socketConnection.ts
|
|
268
|
+
export class SocketConnection {
|
|
269
|
+
private _socket: net.Socket;
|
|
270
|
+
private _pendingBuffers: Buffer[] = [];
|
|
271
|
+
|
|
272
|
+
onclose?: () => void;
|
|
273
|
+
onmessage?: (message: any) => void;
|
|
274
|
+
|
|
275
|
+
constructor(socket: net.Socket) {
|
|
276
|
+
this._socket = socket;
|
|
277
|
+
socket.on('data', buffer => this._onData(buffer));
|
|
278
|
+
socket.on('close', () => this.onclose?.());
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async send(message: { id: number, error?: string, result?: any }) {
|
|
282
|
+
await new Promise((resolve, reject) => {
|
|
283
|
+
this._socket.write(`${JSON.stringify(message)}\n`, error => {
|
|
284
|
+
if (error) reject(error);
|
|
285
|
+
else resolve(undefined);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
close() {
|
|
291
|
+
this._socket.destroy();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Data Flow
|
|
297
|
+
|
|
298
|
+
### 1. 打开浏览器
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
用户执行: cssprobe-cli open https://example.com --headed
|
|
302
|
+
↓
|
|
303
|
+
CLI 程序解析参数
|
|
304
|
+
↓
|
|
305
|
+
Session.startDaemon() 启动 daemon 进程
|
|
306
|
+
↓
|
|
307
|
+
Daemon 进程启动,监听 Unix socket
|
|
308
|
+
↓
|
|
309
|
+
CLI 连接 daemon,发送 goto 命令
|
|
310
|
+
↓
|
|
311
|
+
Daemon 执行导航,返回结果
|
|
312
|
+
↓
|
|
313
|
+
CLI 输出: {"sessionId":"default","pid":12345,"url":"https://example.com"}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 2. 执行 CSS 诊断
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
用户执行: cssprobe-cli inspect .sidebar --json
|
|
320
|
+
↓
|
|
321
|
+
CLI 程序解析参数
|
|
322
|
+
↓
|
|
323
|
+
Session.run() 连接 daemon socket
|
|
324
|
+
↓
|
|
325
|
+
发送命令: { method: 'run', args: { _: ['inspect', '.sidebar'], json: true } }
|
|
326
|
+
↓
|
|
327
|
+
Daemon 接收命令,调用 cssprobe_inspect tool
|
|
328
|
+
↓
|
|
329
|
+
Tool 注入 collector,执行分析
|
|
330
|
+
↓
|
|
331
|
+
返回 JSON 结果
|
|
332
|
+
↓
|
|
333
|
+
CLI 输出诊断结果
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 3. 注入 CSS
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
用户执行: cssprobe-cli inject-css ".sidebar { background: red; }"
|
|
340
|
+
↓
|
|
341
|
+
CLI 程序解析参数
|
|
342
|
+
↓
|
|
343
|
+
Session.run() 连接 daemon socket
|
|
344
|
+
↓
|
|
345
|
+
发送命令: { method: 'run', args: { _: ['inject-css', '.sidebar { background: red; }'] } }
|
|
346
|
+
↓
|
|
347
|
+
Daemon 接收命令,调用 cssprobe_inject_css tool
|
|
348
|
+
↓
|
|
349
|
+
Tool 执行 page.addStyleTag({ content: css })
|
|
350
|
+
↓
|
|
351
|
+
返回成功结果
|
|
352
|
+
↓
|
|
353
|
+
CLI 输出: {"success":true}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## Dependencies
|
|
357
|
+
|
|
358
|
+
| 依赖 | 版本 | 用途 |
|
|
359
|
+
|------|------|------|
|
|
360
|
+
| playwright-core | 已有 | 浏览器 API |
|
|
361
|
+
| zod | 已有 | 参数验证 |
|
|
362
|
+
| debug | 需要添加 | 日志 |
|
|
363
|
+
|
|
364
|
+
## Migration Plan
|
|
365
|
+
|
|
366
|
+
### Phase 1: 基础设施
|
|
367
|
+
1. 复制 socketConnection.ts
|
|
368
|
+
2. 复制 session.ts, registry.ts(简化版)
|
|
369
|
+
3. 复制 daemon.ts
|
|
370
|
+
4. 复制 backend 基础(browserBackend.ts, context.ts, tool.ts, response.ts)
|
|
371
|
+
|
|
372
|
+
### Phase 2: 工具集成
|
|
373
|
+
1. 复制 playwright-cli 工具(evaluate, screenshot, pdf, navigate, tabs)
|
|
374
|
+
2. 实现 cssprobe 工具(inspect, tree, layout, findings, inject-css)
|
|
375
|
+
|
|
376
|
+
### Phase 3: CLI 集成
|
|
377
|
+
1. 合并命令定义
|
|
378
|
+
2. 更新 CLI 入口
|
|
379
|
+
3. 更新输出格式
|
|
380
|
+
|
|
381
|
+
### Phase 4: 测试
|
|
382
|
+
1. 单元测试
|
|
383
|
+
2. 集成测试
|
|
384
|
+
3. 端到端测试
|
|
385
|
+
|
|
386
|
+
## Backward Compatibility
|
|
387
|
+
|
|
388
|
+
| 旧命令 | 新命令 | 说明 |
|
|
389
|
+
|--------|--------|------|
|
|
390
|
+
| `login <url>` | `open <url>` → 手动登录 → `save-state` | 流程变化 |
|
|
391
|
+
| `inspect <url> --wait` | `open <url>` → 手动操作 → `inspect <selector>` | 流程变化 |
|
|
392
|
+
| `interactive <url>` | `open <url>` → 多次 `inspect`/`tree`/`inject-css` | 流程变化 |
|
|
393
|
+
| `state-import` | 保留 | 不变 |
|
|
@@ -238,12 +238,13 @@ confidence: DEFINITE 8 | INDEFINITE 0 | UNVERIFIABLE 1
|
|
|
238
238
|
export CSSPROBE_BROWSER=chromium
|
|
239
239
|
export CSSPROBE_DEPTH=6
|
|
240
240
|
export CSSPROBE_HEADED=true
|
|
241
|
+
export CSSPROBE_VIEWPORT=375x812
|
|
241
242
|
export CSSPROBE_PROFILE=default
|
|
242
243
|
```
|
|
243
244
|
|
|
244
245
|
Priority (highest to lowest):
|
|
245
|
-
1. CLI flags: `--browser`, `--depth`, `--headed`
|
|
246
|
-
2. Environment variables: `CSSPROBE_BROWSER`, `CSSPROBE_DEPTH`, `CSSPROBE_HEADED`
|
|
246
|
+
1. CLI flags: `--browser`, `--depth`, `--headed`, `--viewport`
|
|
247
|
+
2. Environment variables: `CSSPROBE_BROWSER`, `CSSPROBE_DEPTH`, `CSSPROBE_HEADED`, `CSSPROBE_VIEWPORT`
|
|
247
248
|
3. Config file: `~/.cssprobe-clirc`
|
|
248
249
|
|
|
249
250
|
---
|
package/package.json
CHANGED