@pablovitasso/szkrabok 1.0.16 → 1.0.18
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 +11 -12
- package/package.json +16 -16
- package/packages/runtime/package.json +2 -2
- package/scripts/release-commit.js +19 -5
- package/scripts/release-publish.js +5 -2
- package/src/cli.js +227 -146
- package/src/index.js +10 -24
- package/scripts/release-reminder.js +0 -13
package/README.md
CHANGED
|
@@ -80,20 +80,19 @@ session_manage { "action": "open", "sessionName": "my-session" }
|
|
|
80
80
|
browser.run_test { "sessionName": "my-session", "files": ["automation/example.spec.js"] }
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
###
|
|
83
|
+
### CLI
|
|
84
84
|
|
|
85
|
-
`
|
|
85
|
+
`szkrabok` is both the MCP server and the CLI. With no arguments it starts the MCP server; with a subcommand it runs the CLI:
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
szkrabok
|
|
96
|
-
szkrabok install-browser # Install Playwright's Chromium
|
|
88
|
+
szkrabok open <profile> # Launch browser, print CDP endpoint, stay alive
|
|
89
|
+
szkrabok session list # Show all sessions (active + stored)
|
|
90
|
+
szkrabok session inspect <id> # Dump cookie/localStorage counts
|
|
91
|
+
szkrabok session delete <id> # Delete a session
|
|
92
|
+
szkrabok session cleanup --days 30 # Delete sessions unused for N days
|
|
93
|
+
szkrabok endpoint <sessionName> # Print CDP + WS endpoints
|
|
94
|
+
szkrabok detect-browser # List usable Chrome/Chromium installations
|
|
95
|
+
szkrabok install-browser # Install Playwright's Chromium
|
|
97
96
|
```
|
|
98
97
|
|
|
99
98
|
---
|
|
@@ -102,7 +101,7 @@ szkrabok install-browser # Install Playwright's Chromium
|
|
|
102
101
|
|
|
103
102
|
* **`@pablovitasso/szkrabok/runtime`** (`packages/runtime/`): Browser bootstrap, stealth, session pool, MCP client (`mcpConnect`, `spawnClient`, codegen).
|
|
104
103
|
* **Config**: `szkrabok.config.toml` (defaults) deep-merged with `szkrabok.config.local.toml` (machine-specific, gitignored).
|
|
105
|
-
* **Release**: `npm run release:patch` bumps version, then `npm publish
|
|
104
|
+
* **Release**: `npm run deps:update` updates dependencies, `npm run release:patch` bumps version + tags, then `npm run release:publish`.
|
|
106
105
|
|
|
107
106
|
---
|
|
108
107
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pablovitasso/szkrabok",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "Production-grade MCP browser automation layer with persistent sessions and stealth capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
20
|
"bin": {
|
|
21
|
-
"szkrabok": "src/index.js"
|
|
22
|
-
"bebok": "src/cli.js"
|
|
21
|
+
"szkrabok": "src/index.js"
|
|
23
22
|
},
|
|
24
23
|
"engines": {
|
|
25
24
|
"node": ">=20.0.0"
|
|
@@ -37,8 +36,9 @@
|
|
|
37
36
|
"format:check": "prettier --check \"{src,tests}/**/*.{js,json,md}\" \"*.{js,json,md}\"",
|
|
38
37
|
"codegen:mcp": "node packages/runtime/mcp-client/codegen/generate-mcp-tools.mjs",
|
|
39
38
|
"prepack": "git describe --exact-match --tags HEAD 2>/dev/null || (echo 'ERROR: HEAD is not tagged. Run npm run release:patch or release:minor first.' && exit 1)",
|
|
40
|
-
"release:patch": "npm version patch --workspaces --include-workspace-root --ignore-scripts
|
|
41
|
-
"release:minor": "npm version minor --workspaces --include-workspace-root --ignore-scripts
|
|
39
|
+
"release:patch": "npm version patch --workspaces --include-workspace-root --ignore-scripts --no-git-tag-version && node scripts/release-commit.js",
|
|
40
|
+
"release:minor": "npm version minor --workspaces --include-workspace-root --ignore-scripts --no-git-tag-version && node scripts/release-commit.js",
|
|
41
|
+
"deps:update": "npx npm-check-updates -u --workspaces && npm install",
|
|
42
42
|
"release:publish": "node scripts/release-publish.js",
|
|
43
43
|
"test": "npm run test:self && npm run test:auto",
|
|
44
44
|
"test:node": "node --test tests/node/*.test.js",
|
|
@@ -68,26 +68,26 @@
|
|
|
68
68
|
"url": "git+https://github.com/PabloVitasso/szkrabok.git"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
71
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
72
72
|
"dotenv": "^17.3.1",
|
|
73
|
-
"playwright": "^1.
|
|
73
|
+
"playwright": "^1.58.2",
|
|
74
74
|
"playwright-core": "^1.58.2",
|
|
75
75
|
"playwright-extra": "^4.3.6",
|
|
76
|
-
"puppeteer": "^24.
|
|
76
|
+
"puppeteer": "^24.39.0",
|
|
77
77
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
78
78
|
"smol-toml": "^1.6.0",
|
|
79
|
-
"zod": "^3.
|
|
79
|
+
"zod": "^4.3.6"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
|
-
"@eslint/js": "^
|
|
83
|
-
"@types/puppeteer": "^
|
|
84
|
-
"ajv": "^8.
|
|
82
|
+
"@eslint/js": "^10.0.1",
|
|
83
|
+
"@types/puppeteer": "^7.0.4",
|
|
84
|
+
"ajv": "^8.18.0",
|
|
85
85
|
"ajv-formats": "^3.0.1",
|
|
86
|
-
"eslint": "^
|
|
87
|
-
"eslint-config-prettier": "^
|
|
86
|
+
"eslint": "^10.0.3",
|
|
87
|
+
"eslint-config-prettier": "^10.1.8",
|
|
88
88
|
"eslint-plugin-node": "^11.1.0",
|
|
89
|
-
"globals": "^
|
|
90
|
-
"prettier": "^3.
|
|
89
|
+
"globals": "^17.4.0",
|
|
90
|
+
"prettier": "^3.8.1",
|
|
91
91
|
"puppeteer-extra": "^3.3.6"
|
|
92
92
|
},
|
|
93
93
|
"puppeteer": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@szkrabok/runtime",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "szkrabok — browser bootstrap, stealth, session pool, MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"node": ">=20.0.0"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@modelcontextprotocol/sdk": ">=1.
|
|
21
|
+
"@modelcontextprotocol/sdk": ">=1.27.1",
|
|
22
22
|
"chrome-launcher": "*",
|
|
23
23
|
"playwright": "*",
|
|
24
24
|
"playwright-core": "*",
|
|
@@ -3,9 +3,23 @@ import { readFileSync } from 'node:fs';
|
|
|
3
3
|
import { resolve, dirname } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
7
|
+
const pkg = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'));
|
|
8
|
+
const v = pkg.version;
|
|
7
9
|
|
|
8
|
-
execSync(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
);
|
|
10
|
+
const run = cmd => execSync(cmd, { cwd: root, stdio: 'inherit' });
|
|
11
|
+
|
|
12
|
+
// Stage all version-bumped files
|
|
13
|
+
run('git add package.json package-lock.json packages/runtime/package.json');
|
|
14
|
+
|
|
15
|
+
// Single release commit
|
|
16
|
+
run(`git commit -m "chore: release ${v}"`);
|
|
17
|
+
|
|
18
|
+
// Tag on this commit (not the npm version bump commit)
|
|
19
|
+
run(`git tag v${v}`);
|
|
20
|
+
|
|
21
|
+
// Push commit and tag together
|
|
22
|
+
run('git push');
|
|
23
|
+
run(`git push origin v${v}`);
|
|
24
|
+
|
|
25
|
+
console.log(`\nReleased v${v}. Run: npm run release:publish`);
|
|
@@ -3,9 +3,12 @@ import { readFileSync } from 'node:fs';
|
|
|
3
3
|
import { resolve, dirname } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
7
|
+
const pkg = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'));
|
|
7
8
|
const v = pkg.version;
|
|
8
9
|
|
|
10
|
+
const run = cmd => execSync(cmd, { cwd: root, stdio: 'inherit' });
|
|
11
|
+
|
|
9
12
|
try {
|
|
10
13
|
execSync('npm whoami', { stdio: 'pipe' });
|
|
11
14
|
} catch {
|
|
@@ -14,5 +17,5 @@ try {
|
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
console.log(`Publishing @pablovitasso/szkrabok@${v} to npm...`);
|
|
17
|
-
|
|
20
|
+
run('npm publish --access public');
|
|
18
21
|
console.log(`\nDone. https://www.npmjs.com/package/@pablovitasso/szkrabok`);
|
package/src/cli.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
1
|
import { program } from 'commander';
|
|
4
2
|
import path from 'node:path';
|
|
5
3
|
import fs from 'node:fs/promises';
|
|
4
|
+
|
|
6
5
|
import { list, deleteSession, endpoint } from './tools/szkrabok_session.js';
|
|
7
6
|
|
|
8
7
|
const SESSIONS_DIR = path.join(process.cwd(), 'sessions');
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
/* ---------- lazy runtime ---------- */
|
|
10
|
+
|
|
11
|
+
let runtime;
|
|
12
|
+
const getRuntime = async () => {
|
|
13
|
+
if (!runtime) runtime = await import('#runtime');
|
|
14
|
+
return runtime;
|
|
15
|
+
};
|
|
14
16
|
|
|
15
17
|
/* ---------- helpers ---------- */
|
|
16
18
|
|
|
@@ -22,175 +24,254 @@ const readJson = async file => {
|
|
|
22
24
|
}
|
|
23
25
|
};
|
|
24
26
|
|
|
25
|
-
const
|
|
27
|
+
const safe = fn => async (...args) => {
|
|
26
28
|
try {
|
|
27
|
-
await
|
|
28
|
-
}
|
|
29
|
-
|
|
29
|
+
await fn(...args);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.error(err?.message ?? err);
|
|
32
|
+
process.exit(1);
|
|
30
33
|
}
|
|
31
34
|
};
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
const attachShutdown = handle => {
|
|
37
|
+
let closing = false;
|
|
38
|
+
|
|
39
|
+
const shutdown = async () => {
|
|
40
|
+
if (closing) return;
|
|
41
|
+
closing = true;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
await handle.close();
|
|
45
|
+
} finally {
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
process.once('SIGINT', shutdown);
|
|
51
|
+
process.once('SIGTERM', shutdown);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/* ---------- program ---------- */
|
|
34
55
|
|
|
35
56
|
program
|
|
36
|
-
.
|
|
37
|
-
.description('
|
|
38
|
-
.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
57
|
+
.name('szkrabok')
|
|
58
|
+
.description('szkrabok CLI')
|
|
59
|
+
.version('1.0.16');
|
|
60
|
+
|
|
61
|
+
/* ---------- init ---------- */
|
|
62
|
+
|
|
63
|
+
program
|
|
64
|
+
.command('init')
|
|
65
|
+
.description('Scaffold minimal config')
|
|
66
|
+
.action(
|
|
67
|
+
safe(async () => {
|
|
68
|
+
const { init } = await import('./tools/scaffold.js');
|
|
69
|
+
|
|
70
|
+
const result = await init({
|
|
71
|
+
dir: process.cwd(),
|
|
72
|
+
preset: 'minimal',
|
|
73
|
+
install: false,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (result.created.length)
|
|
77
|
+
console.error(`Created: ${result.created.join(', ')}`);
|
|
78
|
+
|
|
79
|
+
if (result.merged.length)
|
|
80
|
+
console.error(`Merged: ${result.merged.join(', ')}`);
|
|
81
|
+
|
|
82
|
+
if (result.skipped.length)
|
|
83
|
+
console.error(`Skipped: ${result.skipped.join(', ')}`);
|
|
84
|
+
|
|
85
|
+
for (const w of result.warnings)
|
|
86
|
+
console.error(`Warning: ${w}`);
|
|
87
|
+
|
|
88
|
+
console.error(
|
|
89
|
+
'Done. Run "szkrabok install-browser" if Chromium is not installed.'
|
|
90
|
+
);
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/* ---------- session ---------- */
|
|
95
|
+
|
|
96
|
+
const session = program.command('session').description('Session management');
|
|
97
|
+
|
|
98
|
+
session
|
|
99
|
+
.command('list')
|
|
100
|
+
.action(
|
|
101
|
+
safe(async () => {
|
|
102
|
+
const { sessions } = await list();
|
|
103
|
+
|
|
104
|
+
console.table(
|
|
105
|
+
sessions.map(s => ({
|
|
106
|
+
ID: s.id,
|
|
107
|
+
Active: s.active ? 'yes' : 'no',
|
|
108
|
+
Preset: s.preset ?? 'N/A',
|
|
109
|
+
Label: s.label ?? 'N/A',
|
|
110
|
+
}))
|
|
111
|
+
);
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
session
|
|
116
|
+
.command('inspect <id>')
|
|
117
|
+
.action(
|
|
118
|
+
safe(async id => {
|
|
119
|
+
const dir = path.join(SESSIONS_DIR, id);
|
|
120
|
+
|
|
121
|
+
const [meta, state] = await Promise.all([
|
|
122
|
+
readJson(path.join(dir, 'meta.json')),
|
|
123
|
+
readJson(path.join(dir, 'state.json')),
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
if (!meta || !state)
|
|
127
|
+
throw new Error(`Session ${id} not found`);
|
|
128
|
+
|
|
129
|
+
console.log('=== METADATA ===');
|
|
130
|
+
console.log(JSON.stringify(meta, null, 2));
|
|
131
|
+
|
|
132
|
+
console.log('\n=== COOKIES ===');
|
|
133
|
+
console.log(state.cookies?.length ?? 0, 'cookies');
|
|
134
|
+
|
|
135
|
+
console.log('\n=== LOCALSTORAGE ===');
|
|
136
|
+
|
|
137
|
+
for (const origin of state.origins ?? []) {
|
|
138
|
+
console.log(
|
|
139
|
+
origin.origin,
|
|
140
|
+
':',
|
|
141
|
+
origin.localStorage?.length ?? 0,
|
|
142
|
+
'items'
|
|
104
143
|
);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
);
|
|
107
147
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
148
|
+
session
|
|
149
|
+
.command('delete <id>')
|
|
150
|
+
.action(
|
|
151
|
+
safe(async id => {
|
|
152
|
+
await deleteSession({ sessionName: id });
|
|
153
|
+
console.log(`Session ${id} deleted`);
|
|
154
|
+
})
|
|
155
|
+
);
|
|
113
156
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
157
|
+
session
|
|
158
|
+
.command('cleanup')
|
|
159
|
+
.option('--days <days>', 'delete sessions older than N days', '30')
|
|
160
|
+
.action(
|
|
161
|
+
safe(async options => {
|
|
162
|
+
const days = Number(options.days) || 30;
|
|
163
|
+
const cutoff = Date.now() - days * 86400000;
|
|
164
|
+
|
|
165
|
+
let entries;
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
entries = await fs.readdir(SESSIONS_DIR, { withFileTypes: true });
|
|
169
|
+
} catch {
|
|
170
|
+
console.log('No sessions directory');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await Promise.all(
|
|
175
|
+
entries
|
|
176
|
+
.filter(e => e.isDirectory())
|
|
177
|
+
.map(async e => {
|
|
178
|
+
const meta = await readJson(
|
|
179
|
+
path.join(SESSIONS_DIR, e.name, 'meta.json')
|
|
180
|
+
);
|
|
121
181
|
|
|
122
|
-
|
|
182
|
+
if (meta?.lastUsed && meta.lastUsed < cutoff) {
|
|
183
|
+
await deleteSession({ sessionName: e.name });
|
|
184
|
+
console.log(`Deleted: ${e.name}`);
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
);
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
/* ---------- open ---------- */
|
|
123
192
|
|
|
124
193
|
program
|
|
125
194
|
.command('open <profile>')
|
|
126
|
-
.description(
|
|
127
|
-
|
|
128
|
-
)
|
|
129
|
-
.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const { launch } = await import('#runtime');
|
|
133
|
-
|
|
134
|
-
const handle = await launch({
|
|
135
|
-
profile,
|
|
136
|
-
preset: options.preset,
|
|
137
|
-
headless: options.headless ?? undefined,
|
|
138
|
-
reuse: false,
|
|
139
|
-
});
|
|
195
|
+
.description('Launch persistent browser and print CDP endpoint')
|
|
196
|
+
.option('--preset <preset>')
|
|
197
|
+
.option('--headless')
|
|
198
|
+
.action(
|
|
199
|
+
safe(async (profile, options) => {
|
|
200
|
+
const { launch } = await getRuntime();
|
|
140
201
|
|
|
141
|
-
|
|
202
|
+
const handle = await launch({
|
|
203
|
+
profile,
|
|
204
|
+
preset: options.preset,
|
|
205
|
+
headless: options.headless ?? undefined,
|
|
206
|
+
reuse: false,
|
|
207
|
+
});
|
|
142
208
|
|
|
143
|
-
|
|
144
|
-
process.on('SIGINT', shutdown);
|
|
145
|
-
process.on('SIGTERM', shutdown);
|
|
209
|
+
console.log(handle.cdpEndpoint);
|
|
146
210
|
|
|
147
|
-
|
|
148
|
-
});
|
|
211
|
+
attachShutdown(handle);
|
|
149
212
|
|
|
150
|
-
|
|
213
|
+
await new Promise(() => {});
|
|
214
|
+
})
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
/* ---------- endpoint ---------- */
|
|
151
218
|
|
|
152
219
|
program
|
|
153
220
|
.command('endpoint <sessionName>')
|
|
154
|
-
.description('Print CDP and WS endpoints
|
|
155
|
-
.action(
|
|
156
|
-
|
|
221
|
+
.description('Print CDP and WS endpoints')
|
|
222
|
+
.action(
|
|
223
|
+
safe(async sessionName => {
|
|
157
224
|
const result = await endpoint({ sessionName });
|
|
225
|
+
|
|
158
226
|
console.log(`CDP: ${result.cdpEndpoint}`);
|
|
159
|
-
if (result.wsEndpoint) console.log(`WS: ${result.wsEndpoint}`);
|
|
160
|
-
} catch (err) {
|
|
161
|
-
console.error(err.message);
|
|
162
|
-
process.exit(1);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
227
|
|
|
166
|
-
|
|
228
|
+
if (result.wsEndpoint)
|
|
229
|
+
console.log(`WS: ${result.wsEndpoint}`);
|
|
230
|
+
})
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
/* ---------- detect browser ---------- */
|
|
167
234
|
|
|
168
235
|
program
|
|
169
236
|
.command('detect-browser')
|
|
170
|
-
.description('Detect
|
|
171
|
-
.action(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (!path) {
|
|
175
|
-
console.log('No usable Chrome/Chromium browser found.\n');
|
|
176
|
-
console.log(' szkrabok install-browser');
|
|
177
|
-
console.log(' https://www.google.com/chrome/\n');
|
|
178
|
-
process.exit(1);
|
|
179
|
-
}
|
|
180
|
-
console.log(path);
|
|
181
|
-
console.log('\nRecommended configuration:\n\n[default]');
|
|
182
|
-
console.log(`executablePath = "${path}"`);
|
|
183
|
-
});
|
|
237
|
+
.description('Detect Chrome/Chromium')
|
|
238
|
+
.action(
|
|
239
|
+
safe(async () => {
|
|
240
|
+
const { findChromiumPath } = await getRuntime();
|
|
184
241
|
|
|
185
|
-
|
|
242
|
+
const chromiumPath = await findChromiumPath();
|
|
243
|
+
|
|
244
|
+
if (!chromiumPath) {
|
|
245
|
+
console.log('No Chromium detected\n');
|
|
246
|
+
console.log(' szkrabok install-browser');
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
console.log(chromiumPath);
|
|
251
|
+
|
|
252
|
+
console.log('\nRecommended config:\n');
|
|
253
|
+
console.log('[default]');
|
|
254
|
+
console.log(`executablePath = "${chromiumPath}"`);
|
|
255
|
+
})
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
/* ---------- install browser ---------- */
|
|
186
259
|
|
|
187
260
|
program
|
|
188
261
|
.command('install-browser')
|
|
189
|
-
.description('Install Chromium
|
|
190
|
-
.action(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
262
|
+
.description('Install Chromium via Playwright')
|
|
263
|
+
.action(() => {
|
|
264
|
+
import('node:child_process').then(({ spawn }) => {
|
|
265
|
+
const proc = spawn('npx', ['playwright', 'install', 'chromium'], {
|
|
266
|
+
stdio: 'inherit',
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
proc.on('close', code => process.exit(code ?? 0));
|
|
270
|
+
});
|
|
194
271
|
});
|
|
195
272
|
|
|
196
|
-
|
|
273
|
+
/* ---------- export ---------- */
|
|
274
|
+
|
|
275
|
+
export async function runCli() {
|
|
276
|
+
await program.parseAsync(process.argv);
|
|
277
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,39 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import 'dotenv/config';
|
|
3
|
-
import { createServer } from './server.js';
|
|
4
|
-
import { log, logError } from './utils/logger.js';
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
const CLI_COMMANDS = new Set(['session', 'open', 'endpoint', 'detect-browser', 'install-browser', 'init']);
|
|
7
5
|
const args = process.argv.slice(2);
|
|
6
|
+
const firstArg = args[0];
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
if (result.merged.length) console.error(`Merged: ${result.merged.join(', ')}`);
|
|
14
|
-
if (result.skipped.length) console.error(`Skipped (already exists): ${result.skipped.join(', ')}`);
|
|
15
|
-
if (result.warnings.length) result.warnings.forEach(w => console.error(`Warning: ${w}`));
|
|
16
|
-
console.error('Done. Run "szkrabok --setup" if Chromium is not yet installed.');
|
|
17
|
-
process.exit(0);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (args.includes('--setup')) {
|
|
21
|
-
const { execSync } = await import('node:child_process');
|
|
22
|
-
console.log('Installing Playwright Chromium browser...');
|
|
23
|
-
try {
|
|
24
|
-
execSync('npx playwright install chromium', { stdio: 'inherit' });
|
|
25
|
-
console.log('Browser installed successfully.');
|
|
26
|
-
} catch {
|
|
27
|
-
console.error('Browser install failed. Run manually: npx playwright install chromium');
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
8
|
+
// --- CLI mode ---
|
|
9
|
+
if (firstArg && (CLI_COMMANDS.has(firstArg) || firstArg === '--setup' || firstArg === '--help' || firstArg === '-h' || firstArg === '--version' || firstArg === '-V')) {
|
|
10
|
+
const { runCli } = await import('./cli.js');
|
|
11
|
+
await runCli();
|
|
30
12
|
process.exit(0);
|
|
31
13
|
}
|
|
32
14
|
|
|
15
|
+
// --- MCP server mode ---
|
|
33
16
|
if (args.includes('--no-headless') || args.includes('--headful')) {
|
|
34
17
|
process.env.HEADLESS = 'false';
|
|
35
18
|
}
|
|
36
19
|
|
|
20
|
+
const { createServer } = await import('./server.js');
|
|
21
|
+
const { log, logError } = await import('./utils/logger.js');
|
|
22
|
+
|
|
37
23
|
const server = createServer();
|
|
38
24
|
|
|
39
25
|
process.on('SIGINT', async () => {
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { resolve, dirname } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
|
|
5
|
-
const pkg = JSON.parse(readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '../package.json'), 'utf8'));
|
|
6
|
-
const v = pkg.version;
|
|
7
|
-
|
|
8
|
-
console.log(`
|
|
9
|
-
Version bumped to ${v}.
|
|
10
|
-
|
|
11
|
-
Next steps:
|
|
12
|
-
npm run release:publish
|
|
13
|
-
`);
|