cssprobe-cli 0.2.6 → 0.2.8

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/dist/help.json CHANGED
@@ -1,39 +1,100 @@
1
1
  {
2
- "global": "Usage: cssprobe-cli <command> [args] [options]\n\n\nInspection:\n inspect <url> [selector] inspect the runtime css of a page (layout, scroll, colors, fonts, backgrounds)\n login <url> open browser for interactive login, then save state for later use\n state-import [file] import cookies from netscape format file into a playwright state file\n interactive <url> open browser and inspect interactively (repl mode)\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 (teaches agents how to use the 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)",
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 resize <width> <height> resize the browser viewport\n eval <expression> evaluate javascript expression on the page (browser context)\n playwright <call> execute playwright api call (node.js context)\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 --viewport viewport size as wxh (e.g. 1280x720)\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
6
+ "flags": {
7
+ "browser": "string",
8
+ "headed": "boolean",
9
+ "viewport": "string"
10
+ },
11
+ "args": [
12
+ "url"
13
+ ]
14
+ },
15
+ "close": {
16
+ "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",
17
+ "flags": {},
18
+ "args": []
19
+ },
20
+ "status": {
21
+ "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",
22
+ "flags": {},
23
+ "args": []
24
+ },
4
25
  "inspect": {
5
- "help": "cssprobe-cli inspect <url> [selector]\n\nInspect the runtime CSS of a page (layout, scroll, colors, fonts, backgrounds)\n\nArguments:\n <url> url, file:// path, or local html file path\n [selector] css selector for the root element (auto-detected if omitted)\nOptions:\n --json output structured json instead of markdown\n --headed show the browser window\n --browser browser engine (default chromium)\n --zoom run 1x/0.5x viewport diagnosis\n --depth dom tree depth (default: auto, max 20)\n --max-nodes node count cap (default 60)\n --up-to ancestor stop tag (default html)\n --state path to saved state file (cookies + localstorage)\n --brief compact output: tree sketch + warnings/errors only\n --layout ascii layout diagram showing element positions and sizes\n --wait wait for user interaction before collecting (implies --headed)\n --viewport viewport size as wxh (e.g. 375x812, default 1280x720)\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
26
+ "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
27
  "flags": {
7
28
  "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
29
  "brief": "boolean",
16
30
  "layout": "boolean",
17
- "wait": "boolean",
18
- "viewport": "string"
31
+ "depth": "string"
19
32
  },
20
33
  "args": [
21
- "url",
22
34
  "selector"
23
35
  ]
24
36
  },
25
- "login": {
26
- "help": "cssprobe-cli login <url>\n\nOpen browser for interactive login, then save state for later use\n\nArguments:\n <url> url, file:// path, or local html file path\nOptions:\n --browser browser engine (default chromium)\n --out output state file path (default: ~/.cssprobe-cli/states/<domain>.json)\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
37
+ "tree": {
38
+ "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",
27
39
  "flags": {
28
- "browser": "string",
29
- "out": "string"
40
+ "depth": "string"
30
41
  },
31
42
  "args": [
32
- "url"
43
+ "selector"
44
+ ]
45
+ },
46
+ "layout": {
47
+ "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",
48
+ "flags": {},
49
+ "args": [
50
+ "selector"
51
+ ]
52
+ },
53
+ "findings": {
54
+ "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",
55
+ "flags": {},
56
+ "args": [
57
+ "selector"
33
58
  ]
34
59
  },
60
+ "inject-css": {
61
+ "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",
62
+ "flags": {},
63
+ "args": [
64
+ "css"
65
+ ]
66
+ },
67
+ "resize": {
68
+ "help": "cssprobe-cli resize <width> <height>\n\nResize the browser viewport\n\nArguments:\n <width> viewport width\n <height> viewport height\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
69
+ "flags": {},
70
+ "args": [
71
+ "width",
72
+ "height"
73
+ ]
74
+ },
75
+ "eval": {
76
+ "help": "cssprobe-cli eval <expression>\n\nEvaluate JavaScript expression on the page (browser context)\n\nArguments:\n <expression> javascript expression to evaluate\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
77
+ "flags": {},
78
+ "args": [
79
+ "expression"
80
+ ]
81
+ },
82
+ "playwright": {
83
+ "help": "cssprobe-cli playwright <call>\n\nExecute Playwright API call (Node.js context)\n\nArguments:\n <call> playwright api call, e.g. \"page.setviewportsize({width: 1280, height: 720})\"\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
84
+ "flags": {},
85
+ "args": [
86
+ "call"
87
+ ]
88
+ },
89
+ "screenshot": {
90
+ "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",
91
+ "flags": {
92
+ "full-page": "boolean"
93
+ },
94
+ "args": []
95
+ },
35
96
  "state-import": {
36
- "help": "cssprobe-cli state-import [file]\n\nImport cookies from Netscape format file into a Playwright state 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",
97
+ "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",
37
98
  "flags": {
38
99
  "out": "string",
39
100
  "merge": "string"
@@ -42,18 +103,6 @@
42
103
  "file"
43
104
  ]
44
105
  },
45
- "interactive": {
46
- "help": "cssprobe-cli interactive <url>\n\nOpen browser and inspect interactively (REPL mode)\n\nArguments:\n <url> url, file:// path, or local html file path\nOptions:\n --browser browser engine (default chromium)\n --state path to saved state file (cookies + localstorage)\n --depth dom tree depth (default: auto, max 20)\n --viewport viewport size as wxh (e.g. 375x812, default 1280x720)\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
47
- "flags": {
48
- "browser": "string",
49
- "state": "string",
50
- "depth": "string",
51
- "viewport": "string"
52
- },
53
- "args": [
54
- "url"
55
- ]
56
- },
57
106
  "config-show": {
58
107
  "help": "cssprobe-cli config-show \n\nShow current configuration\n\nOptions:\n --profile profile name",
59
108
  "flags": {
@@ -96,7 +145,7 @@
96
145
  "args": []
97
146
  },
98
147
  "skill-install": {
99
- "help": "cssprobe-cli skill-install \n\nInstall Agent Skill for cssprobe-cli (teaches agents how to use the 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",
148
+ "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",
100
149
  "flags": {
101
150
  "target": "string",
102
151
  "local": "boolean",
@@ -115,12 +164,11 @@
115
164
  }
116
165
  },
117
166
  "booleanOptions": [
118
- "json",
119
167
  "headed",
120
- "zoom",
168
+ "json",
121
169
  "brief",
122
170
  "layout",
123
- "wait",
171
+ "full-page",
124
172
  "local"
125
173
  ]
126
174
  }
@@ -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` | 保留 | 不变 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cssprobe-cli",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Runtime CSS probe — inspect layout, colors, backgrounds, fonts and more in a live browser. AI agents and shell automation.",
5
5
  "keywords": [
6
6
  "css",