cssprobe-cli 0.2.26 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +24 -0
- package/dist/cli/commands.js +26 -0
- package/dist/cli/program.js +59 -152
- package/dist/cli/session-ops.d.ts +43 -0
- package/dist/cli/session-ops.js +163 -0
- package/dist/help.json +22 -1
- package/dist/installer/index.d.ts +2 -0
- package/dist/installer/index.js +6 -1
- package/dist/installer/mcp.d.ts +14 -0
- package/dist/installer/mcp.js +274 -0
- package/dist/installer/toml.d.ts +20 -0
- package/dist/installer/toml.js +98 -0
- package/dist/mcp/index.d.ts +15 -0
- package/dist/mcp/index.js +174 -0
- package/dist/mcp/server-instructions.d.ts +1 -0
- package/dist/mcp/server-instructions.js +27 -0
- package/dist/mcp/tools.d.ts +261 -0
- package/dist/mcp/tools.js +238 -0
- package/llms.txt +1 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes are documented on the [GitHub Releases](https://github.com/mack-peng/cssprobe-cli/releases) page.
|
|
4
4
|
|
|
5
|
+
## [0.3.0] - 2026-09-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- MCP server (`cssprobe-cli mcp`, stdio): 10 tools — open / inspect / tree / layout / findings / eval / screenshot / inject_css / close / status
|
|
9
|
+
- `mcp-install` / `mcp-uninstall`: auto-configure MCP clients (Claude Code, Cursor, Codex CLI, opencode, Gemini CLI, Hermes)
|
|
10
|
+
- `mcpName` (`io.github.mack-peng/cssprobe-cli`) for the official MCP Registry
|
|
11
|
+
- README MCP section; `llms.txt` MCP pointer
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Session commands (open/close/status/inspect/…) now share one implementation between CLI and MCP (`src/cli/session-ops.ts`); behavior unchanged
|
|
15
|
+
|
|
5
16
|
## [0.2.26] - 2026-09-16
|
|
6
17
|
|
|
7
18
|
### Added
|
package/README.md
CHANGED
|
@@ -119,6 +119,30 @@ cssprobe-cli inspect body --json
|
|
|
119
119
|
cssprobe-cli inspect body --json | jq '.findings[] | {id, confidence, message}'
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
### 4. Use as an MCP Server (AI clients)
|
|
123
|
+
|
|
124
|
+
cssprobe-cli ships an MCP server (stdio), so AI clients call it as a native tool — no shell needed:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
cssprobe-cli mcp-install # auto-detect and configure installed clients
|
|
128
|
+
cssprobe-cli mcp-install --target=claude,cursor,codex
|
|
129
|
+
cssprobe-cli mcp-install --local # write project-scoped config
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Manual configuration for any MCP client:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"mcpServers": {
|
|
137
|
+
"cssprobe-cli": { "command": "cssprobe-cli", "args": ["mcp"] }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Exposes 10 tools: `cssprobe_open`, `cssprobe_inspect`, `cssprobe_tree`, `cssprobe_layout`,
|
|
143
|
+
`cssprobe_findings`, `cssprobe_eval`, `cssprobe_screenshot`, `cssprobe_inject_css`,
|
|
144
|
+
`cssprobe_close`, `cssprobe_status`. Remove with `cssprobe-cli mcp-uninstall`.
|
|
145
|
+
|
|
122
146
|
---
|
|
123
147
|
|
|
124
148
|
## Commands
|
package/dist/cli/commands.js
CHANGED
|
@@ -247,6 +247,30 @@ const skillUninstall = (0, command_1.declareCommand)({
|
|
|
247
247
|
path: z.string().optional().describe('custom base directory'),
|
|
248
248
|
}),
|
|
249
249
|
});
|
|
250
|
+
// ── mcp ──
|
|
251
|
+
const mcp = (0, command_1.declareCommand)({
|
|
252
|
+
name: 'mcp',
|
|
253
|
+
category: 'core',
|
|
254
|
+
description: 'Start MCP server (stdio transport) for AI clients',
|
|
255
|
+
});
|
|
256
|
+
const mcpInstall = (0, command_1.declareCommand)({
|
|
257
|
+
name: 'mcp-install',
|
|
258
|
+
category: 'core',
|
|
259
|
+
description: 'Install MCP server config into AI client configs',
|
|
260
|
+
options: z.object({
|
|
261
|
+
target: z.string().optional().describe('client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)'),
|
|
262
|
+
local: z.boolean().optional().describe('write project-local config instead of global'),
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
const mcpUninstall = (0, command_1.declareCommand)({
|
|
266
|
+
name: 'mcp-uninstall',
|
|
267
|
+
category: 'core',
|
|
268
|
+
description: 'Remove MCP server config from AI client configs',
|
|
269
|
+
options: z.object({
|
|
270
|
+
target: z.string().optional().describe('client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)'),
|
|
271
|
+
local: z.boolean().optional().describe('remove project-local config instead of global'),
|
|
272
|
+
}),
|
|
273
|
+
});
|
|
250
274
|
// ── export ──
|
|
251
275
|
const commandsArray = [
|
|
252
276
|
// session management
|
|
@@ -261,6 +285,8 @@ const commandsArray = [
|
|
|
261
285
|
configShow, configSet, configList, configUse, configNew, configPath,
|
|
262
286
|
// skill
|
|
263
287
|
skillInstall, skillUninstall,
|
|
288
|
+
// mcp
|
|
289
|
+
mcp, mcpInstall, mcpUninstall,
|
|
264
290
|
];
|
|
265
291
|
exports.commands = Object.fromEntries(commandsArray.map(cmd => [cmd.name, cmd]));
|
|
266
292
|
exports.inspectCommand = inspect;
|
package/dist/cli/program.js
CHANGED
|
@@ -13,6 +13,8 @@ const commands_1 = require("./commands");
|
|
|
13
13
|
const output_1 = require("./output");
|
|
14
14
|
const config_1 = require("../config/config");
|
|
15
15
|
const session_1 = require("../daemon/session");
|
|
16
|
+
const session_ops_1 = require("./session-ops");
|
|
17
|
+
const mcp_1 = require("../mcp");
|
|
16
18
|
const globalOptions = ['json', 'raw', 'help', 'h', 'version', 'v', 'p', 'profile'];
|
|
17
19
|
const booleanGlobalOptions = ['help', 'json', 'raw', 'version', 'v', 'h'];
|
|
18
20
|
async function program() {
|
|
@@ -37,8 +39,13 @@ async function program() {
|
|
|
37
39
|
return;
|
|
38
40
|
if (handleSkillCommands(commandName, command, rawArgs, output))
|
|
39
41
|
return;
|
|
42
|
+
if (handleMcpInstallCommands(commandName, command, rawArgs, output))
|
|
43
|
+
return;
|
|
40
44
|
// Session-based commands
|
|
41
45
|
switch (commandName) {
|
|
46
|
+
case 'mcp':
|
|
47
|
+
await (0, mcp_1.runMcpServer)();
|
|
48
|
+
return;
|
|
42
49
|
case 'open':
|
|
43
50
|
await handleOpen(command, rawArgs, output);
|
|
44
51
|
return;
|
|
@@ -111,12 +118,8 @@ async function handleOpen(command, args, output) {
|
|
|
111
118
|
try {
|
|
112
119
|
const cmdArgs = splitArgs(args);
|
|
113
120
|
const parsed = (0, command_1.parseCommand)(command, cmdArgs);
|
|
114
|
-
const url = parsed.url || 'about:blank';
|
|
115
|
-
const browser = parsed.browser || 'chromium';
|
|
116
|
-
const headed = !!parsed.headed;
|
|
117
|
-
const viewportStr = parsed.viewport;
|
|
118
|
-
const state = parsed.state;
|
|
119
121
|
// Parse viewport
|
|
122
|
+
const viewportStr = parsed.viewport;
|
|
120
123
|
let viewport;
|
|
121
124
|
if (viewportStr) {
|
|
122
125
|
const parts = viewportStr.split('x');
|
|
@@ -132,90 +135,41 @@ async function handleOpen(command, args, output) {
|
|
|
132
135
|
}
|
|
133
136
|
viewport = { width, height };
|
|
134
137
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
browser,
|
|
138
|
-
headed,
|
|
138
|
+
const result = await (0, session_ops_1.openSession)({
|
|
139
|
+
url: parsed.url,
|
|
140
|
+
browser: parsed.browser,
|
|
141
|
+
headed: !!parsed.headed,
|
|
139
142
|
viewport,
|
|
140
|
-
state,
|
|
141
|
-
|
|
142
|
-
}, 'open');
|
|
143
|
-
// Wait for the daemon to be ready (poll session file instead of fixed delay)
|
|
144
|
-
const session = await waitForSession(5000);
|
|
145
|
-
if (!session)
|
|
146
|
-
throw new Error('Daemon failed to become ready within 5s. Check the daemon log for errors.');
|
|
147
|
-
await session.run({ _: ['goto', url] });
|
|
148
|
-
const result = {
|
|
149
|
-
sessionId: 'default',
|
|
150
|
-
pid,
|
|
151
|
-
url,
|
|
152
|
-
viewport: viewport || { width: 1280, height: 720 },
|
|
153
|
-
};
|
|
143
|
+
state: parsed.state,
|
|
144
|
+
});
|
|
154
145
|
if (output.json) {
|
|
155
146
|
console.log(JSON.stringify(result, null, 2));
|
|
156
147
|
}
|
|
157
148
|
else {
|
|
158
|
-
console.log(`Browser opened at ${url}`);
|
|
159
|
-
console.log(`Session ID:
|
|
160
|
-
console.log(`PID: ${pid}`);
|
|
161
|
-
console.log(`Viewport: ${viewport
|
|
149
|
+
console.log(`Browser opened at ${result.url}`);
|
|
150
|
+
console.log(`Session ID: ${result.sessionId}`);
|
|
151
|
+
console.log(`PID: ${result.pid}`);
|
|
152
|
+
console.log(`Viewport: ${result.viewport.width}x${result.viewport.height}`);
|
|
162
153
|
}
|
|
163
154
|
}
|
|
164
155
|
catch (e) {
|
|
165
156
|
output.error(e instanceof Error ? e.message : String(e));
|
|
166
157
|
}
|
|
167
158
|
}
|
|
168
|
-
async function waitForSession(timeoutMs) {
|
|
169
|
-
const deadline = Date.now() + timeoutMs;
|
|
170
|
-
while (Date.now() < deadline) {
|
|
171
|
-
const session = await (0, session_1.loadSession)();
|
|
172
|
-
if (session && await session.canConnect())
|
|
173
|
-
return session;
|
|
174
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
175
|
-
}
|
|
176
|
-
return undefined;
|
|
177
|
-
}
|
|
178
159
|
async function handleClose(command, args, output) {
|
|
179
160
|
try {
|
|
180
161
|
const cmdArgs = splitArgs(args);
|
|
181
162
|
const parsed = (0, command_1.parseCommand)(command, cmdArgs);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
for (const session of sessions) {
|
|
190
|
-
try {
|
|
191
|
-
await session.stop();
|
|
192
|
-
results.push({ sessionId: session.name, closed: true });
|
|
193
|
-
}
|
|
194
|
-
catch (e) {
|
|
195
|
-
results.push({ sessionId: session.name, closed: false, error: e.message });
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
if (output.json) {
|
|
199
|
-
console.log(JSON.stringify({ closed: results }, null, 2));
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
const closedCount = results.filter(r => r.closed).length;
|
|
203
|
-
console.log(`Closed ${closedCount} session(s).`);
|
|
204
|
-
}
|
|
163
|
+
const result = await (0, session_ops_1.closeSession)(!!parsed.all);
|
|
164
|
+
if (output.json) {
|
|
165
|
+
console.log(JSON.stringify(parsed.all ? { closed: result.sessions } : { closed: true, sessionId: 'default' }, null, 2));
|
|
166
|
+
}
|
|
167
|
+
else if (parsed.all) {
|
|
168
|
+
const closedCount = result.sessions.filter(r => r.closed).length;
|
|
169
|
+
console.log(`Closed ${closedCount} session(s).`);
|
|
205
170
|
}
|
|
206
171
|
else {
|
|
207
|
-
|
|
208
|
-
if (!session) {
|
|
209
|
-
output.error('No active session. Run: cssprobe-cli open <url>');
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
await session.stop();
|
|
213
|
-
if (output.json) {
|
|
214
|
-
console.log(JSON.stringify({ closed: true, sessionId: 'default' }, null, 2));
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
console.log('Browser closed.');
|
|
218
|
-
}
|
|
172
|
+
console.log('Browser closed.');
|
|
219
173
|
}
|
|
220
174
|
}
|
|
221
175
|
catch (e) {
|
|
@@ -224,32 +178,19 @@ async function handleClose(command, args, output) {
|
|
|
224
178
|
}
|
|
225
179
|
async function handleStatus(output) {
|
|
226
180
|
try {
|
|
227
|
-
const
|
|
228
|
-
if (!session) {
|
|
229
|
-
const result = { sessionId: 'default', alive: false };
|
|
230
|
-
if (output.json) {
|
|
231
|
-
console.log(JSON.stringify(result, null, 2));
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
console.log('No active session.');
|
|
235
|
-
}
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
const canConnect = await session.canConnect();
|
|
239
|
-
const result = {
|
|
240
|
-
sessionId: 'default',
|
|
241
|
-
alive: canConnect,
|
|
242
|
-
config: session.config,
|
|
243
|
-
};
|
|
181
|
+
const result = await (0, session_ops_1.getSessionStatus)();
|
|
244
182
|
if (output.json) {
|
|
245
183
|
console.log(JSON.stringify(result, null, 2));
|
|
184
|
+
return;
|
|
246
185
|
}
|
|
247
|
-
|
|
248
|
-
console.log(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
186
|
+
if (!result.config) {
|
|
187
|
+
console.log('No active session.');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
console.log(`Session: default`);
|
|
191
|
+
console.log(`Status: ${result.alive ? 'alive' : 'dead'}`);
|
|
192
|
+
if (result.alive) {
|
|
193
|
+
console.log(`Browser: ${result.config.browser.browserName}`);
|
|
253
194
|
}
|
|
254
195
|
}
|
|
255
196
|
catch (e) {
|
|
@@ -258,49 +199,12 @@ async function handleStatus(output) {
|
|
|
258
199
|
}
|
|
259
200
|
async function handleSessionCommand(commandName, command, args, output) {
|
|
260
201
|
try {
|
|
261
|
-
const session = await (0, session_1.loadSession)();
|
|
262
|
-
if (!session) {
|
|
263
|
-
output.error('No active session. Run: cssprobe-cli open <url>');
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
202
|
const cmdArgs = splitArgs(args);
|
|
267
203
|
const parsed = (0, command_1.parseCommand)(command, cmdArgs);
|
|
268
|
-
|
|
269
|
-
const daemonArgs = [commandName];
|
|
270
|
-
// Add positional args
|
|
271
|
-
if (command.args) {
|
|
272
|
-
const argsSchema = command.args;
|
|
273
|
-
const argNames = Object.keys(argsSchema.shape);
|
|
274
|
-
for (const name of argNames) {
|
|
275
|
-
if (parsed[name] !== undefined) {
|
|
276
|
-
daemonArgs.push(String(parsed[name]));
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
// Add options
|
|
281
|
-
if (command.options) {
|
|
282
|
-
const optionsSchema = command.options;
|
|
283
|
-
const optionNames = Object.keys(optionsSchema.shape);
|
|
284
|
-
for (const name of optionNames) {
|
|
285
|
-
if (parsed[name] !== undefined) {
|
|
286
|
-
if (typeof parsed[name] === 'boolean') {
|
|
287
|
-
if (parsed[name])
|
|
288
|
-
daemonArgs.push(`--${name}`);
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
daemonArgs.push(`--${name}=${parsed[name]}`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
// Run command in session
|
|
297
|
-
const runArgs = { ...cmdArgs, _: daemonArgs };
|
|
298
|
-
if (args.json)
|
|
299
|
-
runArgs.json = true;
|
|
300
|
-
const result = await session.run(runArgs);
|
|
204
|
+
const result = await (0, session_ops_1.runSessionCommand)(commandName, command, parsed, !!args.json);
|
|
301
205
|
// Screenshot: save the base64 data URL to a file and report the path
|
|
302
206
|
if (commandName === 'screenshot' && typeof result.text === 'string' && result.text.startsWith('data:image/png;base64,')) {
|
|
303
|
-
const saved = saveScreenshotFile(result.text, parsed.out);
|
|
207
|
+
const saved = (0, session_ops_1.saveScreenshotFile)(result.text, parsed.out);
|
|
304
208
|
if (output.json) {
|
|
305
209
|
console.log(JSON.stringify({ saved }, null, 2));
|
|
306
210
|
}
|
|
@@ -315,24 +219,6 @@ async function handleSessionCommand(commandName, command, args, output) {
|
|
|
315
219
|
output.error(e instanceof Error ? e.message : String(e));
|
|
316
220
|
}
|
|
317
221
|
}
|
|
318
|
-
function saveScreenshotFile(dataUrl, out) {
|
|
319
|
-
const base64 = dataUrl.replace(/^data:image\/png;base64,/, '');
|
|
320
|
-
const buffer = Buffer.from(base64, 'base64');
|
|
321
|
-
const resolvedOut = out
|
|
322
|
-
? path_1.default.resolve(out)
|
|
323
|
-
: (() => {
|
|
324
|
-
const dir = path_1.default.join(os_1.default.homedir(), '.cssprobe-cli', 'screenshots');
|
|
325
|
-
if (!fs_1.default.existsSync(dir))
|
|
326
|
-
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
327
|
-
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
328
|
-
return path_1.default.join(dir, `screenshot-${ts}.png`);
|
|
329
|
-
})();
|
|
330
|
-
const dir = path_1.default.dirname(resolvedOut);
|
|
331
|
-
if (!fs_1.default.existsSync(dir))
|
|
332
|
-
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
333
|
-
fs_1.default.writeFileSync(resolvedOut, buffer);
|
|
334
|
-
return resolvedOut;
|
|
335
|
-
}
|
|
336
222
|
// ── config commands ──
|
|
337
223
|
function handleConfigCommands(commandName, command, args, output) {
|
|
338
224
|
try {
|
|
@@ -426,6 +312,27 @@ function handleSkillCommands(commandName, command, args, output) {
|
|
|
426
312
|
}
|
|
427
313
|
return false;
|
|
428
314
|
}
|
|
315
|
+
// ── mcp-install commands ──
|
|
316
|
+
function handleMcpInstallCommands(commandName, command, args, output) {
|
|
317
|
+
if (commandName !== 'mcp-install' && commandName !== 'mcp-uninstall')
|
|
318
|
+
return false;
|
|
319
|
+
const targetFlag = args.target || 'auto';
|
|
320
|
+
const loc = args.local ? 'local' : 'global';
|
|
321
|
+
try {
|
|
322
|
+
const result = commandName === 'mcp-install'
|
|
323
|
+
? (0, installer_1.installMcpConfig)(loc, targetFlag)
|
|
324
|
+
: (0, installer_1.uninstallMcpConfig)(loc, targetFlag);
|
|
325
|
+
const summary = result.files.map(f => `${f.agent}: ${f.action} ${f.path}`);
|
|
326
|
+
for (const note of result.notes)
|
|
327
|
+
summary.push(`note: ${note}`);
|
|
328
|
+
console.log(output.format({ files: summary }));
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
catch (e) {
|
|
332
|
+
output.error(e instanceof Error ? e.message : String(e));
|
|
333
|
+
}
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
429
336
|
function parseNetscapeCookies(content) {
|
|
430
337
|
const cookies = [];
|
|
431
338
|
const lines = content.split('\n');
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Session } from '../daemon/session';
|
|
2
|
+
import type { SessionConfig } from '../daemon/session';
|
|
3
|
+
import type { AnyCommandSchema } from './command';
|
|
4
|
+
export interface OpenOptions {
|
|
5
|
+
url?: string;
|
|
6
|
+
browser?: string;
|
|
7
|
+
headed?: boolean;
|
|
8
|
+
viewport?: {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
};
|
|
12
|
+
state?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface OpenResult {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
pid?: number;
|
|
17
|
+
url: string;
|
|
18
|
+
viewport: {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare function openSession(opts: OpenOptions): Promise<OpenResult>;
|
|
24
|
+
export declare function waitForSession(timeoutMs: number): Promise<Session | undefined>;
|
|
25
|
+
export interface CloseResult {
|
|
26
|
+
sessions: Array<{
|
|
27
|
+
sessionId: string;
|
|
28
|
+
closed: boolean;
|
|
29
|
+
error?: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
export declare function closeSession(all?: boolean): Promise<CloseResult>;
|
|
33
|
+
export interface StatusResult {
|
|
34
|
+
sessionId: string;
|
|
35
|
+
alive: boolean;
|
|
36
|
+
config?: SessionConfig;
|
|
37
|
+
}
|
|
38
|
+
export declare function getSessionStatus(): Promise<StatusResult>;
|
|
39
|
+
export declare function buildDaemonArgs(commandName: string, command: AnyCommandSchema, parsed: Record<string, unknown>): string[];
|
|
40
|
+
export declare function runSessionCommand(commandName: string, command: AnyCommandSchema, parsed: Record<string, unknown>, json?: boolean): Promise<{
|
|
41
|
+
text: string;
|
|
42
|
+
}>;
|
|
43
|
+
export declare function saveScreenshotFile(dataUrl: string, out?: string): string;
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
exports.openSession = openSession;
|
|
37
|
+
exports.waitForSession = waitForSession;
|
|
38
|
+
exports.closeSession = closeSession;
|
|
39
|
+
exports.getSessionStatus = getSessionStatus;
|
|
40
|
+
exports.buildDaemonArgs = buildDaemonArgs;
|
|
41
|
+
exports.runSessionCommand = runSessionCommand;
|
|
42
|
+
exports.saveScreenshotFile = saveScreenshotFile;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const os = __importStar(require("os"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const session_1 = require("../daemon/session");
|
|
47
|
+
async function openSession(opts) {
|
|
48
|
+
const url = opts.url || 'about:blank';
|
|
49
|
+
const browser = opts.browser || 'chromium';
|
|
50
|
+
const { pid } = await session_1.Session.startDaemon({
|
|
51
|
+
browser,
|
|
52
|
+
headed: !!opts.headed,
|
|
53
|
+
viewport: opts.viewport,
|
|
54
|
+
state: opts.state,
|
|
55
|
+
_: ['open', url],
|
|
56
|
+
}, 'open');
|
|
57
|
+
const session = await waitForSession(5000);
|
|
58
|
+
if (!session)
|
|
59
|
+
throw new Error('Daemon failed to become ready within 5s. Check the daemon log for errors.');
|
|
60
|
+
await session.run({ _: ['goto', url] });
|
|
61
|
+
return {
|
|
62
|
+
sessionId: 'default',
|
|
63
|
+
pid,
|
|
64
|
+
url,
|
|
65
|
+
viewport: opts.viewport || { width: 1280, height: 720 },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async function waitForSession(timeoutMs) {
|
|
69
|
+
const deadline = Date.now() + timeoutMs;
|
|
70
|
+
while (Date.now() < deadline) {
|
|
71
|
+
const session = await (0, session_1.loadSession)();
|
|
72
|
+
if (session && await session.canConnect())
|
|
73
|
+
return session;
|
|
74
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
async function closeSession(all = false) {
|
|
79
|
+
if (all) {
|
|
80
|
+
const sessions = await (0, session_1.listAllSessions)();
|
|
81
|
+
if (sessions.length === 0)
|
|
82
|
+
throw new Error('No active sessions found.');
|
|
83
|
+
const results = [];
|
|
84
|
+
for (const session of sessions) {
|
|
85
|
+
try {
|
|
86
|
+
await session.stop();
|
|
87
|
+
results.push({ sessionId: session.name, closed: true });
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
results.push({ sessionId: session.name, closed: false, error: e.message });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { sessions: results };
|
|
94
|
+
}
|
|
95
|
+
const session = await (0, session_1.loadSession)();
|
|
96
|
+
if (!session)
|
|
97
|
+
throw new Error('No active session. Run: cssprobe-cli open <url>');
|
|
98
|
+
await session.stop();
|
|
99
|
+
return { sessions: [{ sessionId: 'default', closed: true }] };
|
|
100
|
+
}
|
|
101
|
+
async function getSessionStatus() {
|
|
102
|
+
const session = await (0, session_1.loadSession)();
|
|
103
|
+
if (!session)
|
|
104
|
+
return { sessionId: 'default', alive: false };
|
|
105
|
+
const canConnect = await session.canConnect();
|
|
106
|
+
return {
|
|
107
|
+
sessionId: 'default',
|
|
108
|
+
alive: canConnect,
|
|
109
|
+
config: session.config,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function buildDaemonArgs(commandName, command, parsed) {
|
|
113
|
+
const daemonArgs = [commandName];
|
|
114
|
+
if (command.args) {
|
|
115
|
+
for (const name of Object.keys(command.args.shape)) {
|
|
116
|
+
if (parsed[name] !== undefined)
|
|
117
|
+
daemonArgs.push(String(parsed[name]));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (command.options) {
|
|
121
|
+
for (const name of Object.keys(command.options.shape)) {
|
|
122
|
+
if (parsed[name] === undefined)
|
|
123
|
+
continue;
|
|
124
|
+
if (typeof parsed[name] === 'boolean') {
|
|
125
|
+
if (parsed[name])
|
|
126
|
+
daemonArgs.push(`--${name}`);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
daemonArgs.push(`--${name}=${parsed[name]}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return daemonArgs;
|
|
134
|
+
}
|
|
135
|
+
async function runSessionCommand(commandName, command, parsed, json = false) {
|
|
136
|
+
const session = await (0, session_1.loadSession)();
|
|
137
|
+
if (!session)
|
|
138
|
+
throw new Error('No active session. Run: cssprobe-cli open <url>');
|
|
139
|
+
const daemonArgs = buildDaemonArgs(commandName, command, parsed);
|
|
140
|
+
const runArgs = { ...parsed, _: daemonArgs };
|
|
141
|
+
if (json)
|
|
142
|
+
runArgs.json = true;
|
|
143
|
+
return await session.run(runArgs);
|
|
144
|
+
}
|
|
145
|
+
function saveScreenshotFile(dataUrl, out) {
|
|
146
|
+
const base64 = dataUrl.replace(/^data:image\/png;base64,/, '');
|
|
147
|
+
const buffer = Buffer.from(base64, 'base64');
|
|
148
|
+
const resolvedOut = out
|
|
149
|
+
? path.resolve(out)
|
|
150
|
+
: (() => {
|
|
151
|
+
const dir = path.join(os.homedir(), '.cssprobe-cli', 'screenshots');
|
|
152
|
+
if (!fs.existsSync(dir))
|
|
153
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
154
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
155
|
+
return path.join(dir, `screenshot-${ts}.png`);
|
|
156
|
+
})();
|
|
157
|
+
const dir = path.dirname(resolvedOut);
|
|
158
|
+
if (!fs.existsSync(dir))
|
|
159
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
160
|
+
fs.writeFileSync(resolvedOut, buffer);
|
|
161
|
+
return resolvedOut;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=session-ops.js.map
|
package/dist/help.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
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 state-save save current browser session state (cookies + localstorage)\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)",
|
|
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 state-save save current browser session state (cookies + localstorage)\n mcp start mcp server (stdio transport) for ai clients\n mcp-install install mcp server config into ai client configs\n mcp-uninstall remove mcp server config from ai client configs\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
4
|
"open": {
|
|
5
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 --state path to saved state file (cookies + localstorage)\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
@@ -173,6 +173,27 @@
|
|
|
173
173
|
"path": "string"
|
|
174
174
|
},
|
|
175
175
|
"args": []
|
|
176
|
+
},
|
|
177
|
+
"mcp": {
|
|
178
|
+
"help": "cssprobe-cli mcp \n\nStart MCP server (stdio transport) for AI clients\n\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
179
|
+
"flags": {},
|
|
180
|
+
"args": []
|
|
181
|
+
},
|
|
182
|
+
"mcp-install": {
|
|
183
|
+
"help": "cssprobe-cli mcp-install \n\nInstall MCP server config into AI client configs\n\nOptions:\n --target client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)\n --local write project-local config instead of global\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
184
|
+
"flags": {
|
|
185
|
+
"target": "string",
|
|
186
|
+
"local": "boolean"
|
|
187
|
+
},
|
|
188
|
+
"args": []
|
|
189
|
+
},
|
|
190
|
+
"mcp-uninstall": {
|
|
191
|
+
"help": "cssprobe-cli mcp-uninstall \n\nRemove MCP server config from AI client configs\n\nOptions:\n --target client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)\n --local remove project-local config instead of global\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
|
|
192
|
+
"flags": {
|
|
193
|
+
"target": "string",
|
|
194
|
+
"local": "boolean"
|
|
195
|
+
},
|
|
196
|
+
"args": []
|
|
176
197
|
}
|
|
177
198
|
},
|
|
178
199
|
"booleanOptions": [
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { buildSkillMd, buildPitfallsMd } from './skill-template';
|
|
2
2
|
export { ALL_TARGETS, getTarget, listTargetIds, resolveTargetFlag } from './targets/registry';
|
|
3
3
|
export type { AgentTarget, Location, TargetId, WriteResult } from './targets/types';
|
|
4
|
+
export { installMcpConfig, uninstallMcpConfig, listMcpClientIds, MCP_SERVER_NAME } from './mcp';
|
|
5
|
+
export type { McpCommandResult, McpFileResult } from './mcp';
|
package/dist/installer/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveTargetFlag = exports.listTargetIds = exports.getTarget = exports.ALL_TARGETS = exports.buildPitfallsMd = exports.buildSkillMd = void 0;
|
|
3
|
+
exports.MCP_SERVER_NAME = exports.listMcpClientIds = exports.uninstallMcpConfig = exports.installMcpConfig = exports.resolveTargetFlag = exports.listTargetIds = exports.getTarget = exports.ALL_TARGETS = exports.buildPitfallsMd = exports.buildSkillMd = void 0;
|
|
4
4
|
var skill_template_1 = require("./skill-template");
|
|
5
5
|
Object.defineProperty(exports, "buildSkillMd", { enumerable: true, get: function () { return skill_template_1.buildSkillMd; } });
|
|
6
6
|
Object.defineProperty(exports, "buildPitfallsMd", { enumerable: true, get: function () { return skill_template_1.buildPitfallsMd; } });
|
|
@@ -9,4 +9,9 @@ Object.defineProperty(exports, "ALL_TARGETS", { enumerable: true, get: function
|
|
|
9
9
|
Object.defineProperty(exports, "getTarget", { enumerable: true, get: function () { return registry_1.getTarget; } });
|
|
10
10
|
Object.defineProperty(exports, "listTargetIds", { enumerable: true, get: function () { return registry_1.listTargetIds; } });
|
|
11
11
|
Object.defineProperty(exports, "resolveTargetFlag", { enumerable: true, get: function () { return registry_1.resolveTargetFlag; } });
|
|
12
|
+
var mcp_1 = require("./mcp");
|
|
13
|
+
Object.defineProperty(exports, "installMcpConfig", { enumerable: true, get: function () { return mcp_1.installMcpConfig; } });
|
|
14
|
+
Object.defineProperty(exports, "uninstallMcpConfig", { enumerable: true, get: function () { return mcp_1.uninstallMcpConfig; } });
|
|
15
|
+
Object.defineProperty(exports, "listMcpClientIds", { enumerable: true, get: function () { return mcp_1.listMcpClientIds; } });
|
|
16
|
+
Object.defineProperty(exports, "MCP_SERVER_NAME", { enumerable: true, get: function () { return mcp_1.MCP_SERVER_NAME; } });
|
|
12
17
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Location, TargetId } from './targets/types';
|
|
2
|
+
export declare const MCP_SERVER_NAME = "cssprobe-cli";
|
|
3
|
+
export interface McpFileResult {
|
|
4
|
+
path: string;
|
|
5
|
+
action: 'created' | 'updated' | 'unchanged' | 'removed' | 'not-found' | 'kept';
|
|
6
|
+
agent: string;
|
|
7
|
+
}
|
|
8
|
+
export interface McpCommandResult {
|
|
9
|
+
files: McpFileResult[];
|
|
10
|
+
notes: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function installMcpConfig(loc: Location, targetFlag?: string): McpCommandResult;
|
|
13
|
+
export declare function uninstallMcpConfig(loc: Location, targetFlag?: string): McpCommandResult;
|
|
14
|
+
export declare function listMcpClientIds(): TargetId[];
|