@haposoft/cafekit 0.11.7 → 0.11.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/bin/lib/i18n.js +3 -0
- package/bin/lib/skill-deps.js +64 -5
- package/bin/phases/skills-setup.js +22 -7
- package/package.json +1 -1
- package/src/claude/skills/chrome-devtools/SKILL.md +33 -0
- package/src/claude/skills/chrome-devtools/scripts/.env.example +20 -0
- package/src/claude/skills/chrome-devtools/scripts/lib/browser.js +61 -0
package/bin/lib/i18n.js
CHANGED
|
@@ -75,6 +75,7 @@ const MESSAGES = {
|
|
|
75
75
|
chromiumInstalling: 'Downloading Chromium for chrome-devtools',
|
|
76
76
|
chromiumReady: 'Chromium ready',
|
|
77
77
|
chromiumSkipped: 'Chromium download skipped — chrome-devtools will prompt on first use.',
|
|
78
|
+
chromiumSystemChromeFound: 'System Chrome detected ({path}), skipping download',
|
|
78
79
|
optionalToolsTitle: 'Optional tools to install manually',
|
|
79
80
|
|
|
80
81
|
// Summary stats
|
|
@@ -180,6 +181,7 @@ const MESSAGES = {
|
|
|
180
181
|
chromiumInstalling: 'chrome-devtools 用 Chromium をダウンロード中',
|
|
181
182
|
chromiumReady: 'Chromium の準備ができました',
|
|
182
183
|
chromiumSkipped: 'Chromium のダウンロードをスキップ — 初回使用時に chrome-devtools が自動取得します。',
|
|
184
|
+
chromiumSystemChromeFound: 'システムChromeを検出 ({path})、ダウンロードをスキップします',
|
|
183
185
|
optionalToolsTitle: '手動でインストールが必要なオプションツール',
|
|
184
186
|
|
|
185
187
|
summaryTitle: 'インストール完了',
|
|
@@ -282,6 +284,7 @@ const MESSAGES = {
|
|
|
282
284
|
chromiumInstalling: 'Đang tải Chromium cho chrome-devtools',
|
|
283
285
|
chromiumReady: 'Chromium đã sẵn sàng',
|
|
284
286
|
chromiumSkipped: 'Bỏ qua tải Chromium — chrome-devtools sẽ tự tải khi dùng lần đầu.',
|
|
287
|
+
chromiumSystemChromeFound: 'Đã phát hiện Chrome hệ thống ({path}), bỏ qua tải Chromium',
|
|
285
288
|
optionalToolsTitle: 'Công cụ tuỳ chọn cần cài thủ công',
|
|
286
289
|
|
|
287
290
|
summaryTitle: 'Cài đặt hoàn tất',
|
package/bin/lib/skill-deps.js
CHANGED
|
@@ -77,9 +77,10 @@ function pipInstall(skillsDir, requirementsPath, upgrade = false) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/** `npm install` (fresh) or `npm update` (existing) in a skill scripts dir. */
|
|
80
|
-
function npmInstall(dir, upgrade = false) {
|
|
80
|
+
function npmInstall(dir, upgrade = false, extraEnv = {}) {
|
|
81
81
|
const args = upgrade ? ['update', '--no-audit', '--no-fund'] : ['install', '--no-audit', '--no-fund'];
|
|
82
|
-
|
|
82
|
+
const env = { ...process.env, ...extraEnv };
|
|
83
|
+
return run('npm', args, { cwd: dir, env }).ok;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
/**
|
|
@@ -88,14 +89,22 @@ function npmInstall(dir, upgrade = false) {
|
|
|
88
89
|
* fall back to the bundled @puppeteer/browsers CLI. `npx puppeteer ...` is
|
|
89
90
|
* avoided — puppeteer ships no `puppeteer` bin under that invocation and `--yes`
|
|
90
91
|
* would refetch from the registry.
|
|
92
|
+
*
|
|
93
|
+
* When `interactive` is true, stdio is inherited so the native progress bar
|
|
94
|
+
* renders directly on the user's terminal (TTY-only, no pipe capture).
|
|
91
95
|
*/
|
|
92
|
-
function installChromium(scriptsDir) {
|
|
96
|
+
function installChromium(scriptsDir, interactive = false) {
|
|
97
|
+
const stdioOpt = interactive ? 'inherit' : 'pipe';
|
|
93
98
|
// Absolute path so it resolves regardless of the child process cwd.
|
|
94
99
|
const installMjs = path.resolve(scriptsDir, 'node_modules', 'puppeteer', 'install.mjs');
|
|
95
100
|
if (fs.existsSync(installMjs)) {
|
|
96
|
-
|
|
101
|
+
const opts = { cwd: scriptsDir, stdio: stdioOpt };
|
|
102
|
+
if (interactive) opts.encoding = 'utf8';
|
|
103
|
+
return run('node', [installMjs], opts).ok;
|
|
97
104
|
}
|
|
98
|
-
|
|
105
|
+
const opts = { cwd: scriptsDir, stdio: stdioOpt };
|
|
106
|
+
if (interactive) opts.encoding = 'utf8';
|
|
107
|
+
return run('npx', ['--yes', '@puppeteer/browsers', 'install', 'chrome'], opts).ok;
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
/** Does a skill scripts dir declare a given npm dependency? */
|
|
@@ -113,6 +122,55 @@ function installPlaywrightBrowser(scriptsDir) {
|
|
|
113
122
|
return run('npx', ['--yes', 'playwright', 'install', 'chromium'], { cwd: scriptsDir }).ok;
|
|
114
123
|
}
|
|
115
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Detect system Chrome/Chromium installation (cross-platform).
|
|
127
|
+
* Returns the executable path if found, undefined otherwise.
|
|
128
|
+
* Supports macOS, Windows, Linux (including snap + PATH fallback).
|
|
129
|
+
*/
|
|
130
|
+
function detectSystemChrome() {
|
|
131
|
+
const candidates = (() => {
|
|
132
|
+
switch (process.platform) {
|
|
133
|
+
case 'darwin':
|
|
134
|
+
return [
|
|
135
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
136
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
137
|
+
];
|
|
138
|
+
case 'win32': {
|
|
139
|
+
const pf = process.env.PROGRAMFILES;
|
|
140
|
+
const pf86 = process.env['PROGRAMFILES(X86)'];
|
|
141
|
+
const local = process.env.LOCALAPPDATA;
|
|
142
|
+
return [
|
|
143
|
+
pf && `${pf}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
144
|
+
pf86 && `${pf86}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
145
|
+
local && `${local}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
146
|
+
].filter(Boolean);
|
|
147
|
+
}
|
|
148
|
+
default: // Linux & others
|
|
149
|
+
return [
|
|
150
|
+
'/usr/bin/google-chrome-stable',
|
|
151
|
+
'/usr/bin/google-chrome',
|
|
152
|
+
'/usr/bin/chromium-browser',
|
|
153
|
+
'/usr/bin/chromium',
|
|
154
|
+
'/snap/bin/chromium',
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
|
|
159
|
+
for (const p of candidates) {
|
|
160
|
+
try { if (fs.existsSync(p)) return p; } catch { /* skip */ }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Linux: last resort — check PATH
|
|
164
|
+
if (process.platform === 'linux') {
|
|
165
|
+
for (const cmd of ['google-chrome', 'google-chrome-stable', 'chromium-browser', 'chromium']) {
|
|
166
|
+
const r = run(isWindows ? 'where' : 'which', [cmd]);
|
|
167
|
+
if (r.ok && r.stdout.trim()) return r.stdout.trim();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
116
174
|
/**
|
|
117
175
|
* List installed skills that ship a runnable scripts/requirements.txt
|
|
118
176
|
* (excludes the tests/ requirements). Returns [{ skill, file }].
|
|
@@ -176,6 +234,7 @@ module.exports = {
|
|
|
176
234
|
pipInstall,
|
|
177
235
|
npmInstall,
|
|
178
236
|
installChromium,
|
|
237
|
+
detectSystemChrome,
|
|
179
238
|
collectRequirements,
|
|
180
239
|
collectSkillPackages,
|
|
181
240
|
pkgHasDep,
|
|
@@ -66,9 +66,20 @@ function setupPython(ctx, skillsDir) {
|
|
|
66
66
|
|
|
67
67
|
/** Run skill-local npm installs/upgrades + browser binaries for a platform's skills. */
|
|
68
68
|
function setupNode(ctx, skillsDir) {
|
|
69
|
+
// Detect system Chrome before npm install — skip ~300MB Chromium download if found
|
|
70
|
+
const chromeScripts = path.join(skillsDir, 'chrome-devtools', 'scripts');
|
|
71
|
+
const hasChromePkg = fs.existsSync(path.join(chromeScripts, 'package.json'));
|
|
72
|
+
const systemChrome = hasChromePkg ? dep.detectSystemChrome() : null;
|
|
73
|
+
|
|
69
74
|
for (const { skill, dir, hasNodeModules } of dep.collectSkillPackages(skillsDir)) {
|
|
70
75
|
ctx.ui.startSpinner(ctx.t('npmInstalling', { skill }));
|
|
71
|
-
|
|
76
|
+
|
|
77
|
+
// chrome-devtools: skip puppeteer postinstall Chromium download if system Chrome exists
|
|
78
|
+
const extraEnv = (skill === 'chrome-devtools' && systemChrome)
|
|
79
|
+
? { PUPPETEER_SKIP_DOWNLOAD: 'true' }
|
|
80
|
+
: {};
|
|
81
|
+
const ok = dep.npmInstall(dir, hasNodeModules, extraEnv);
|
|
82
|
+
|
|
72
83
|
if (ok) {
|
|
73
84
|
ctx.ui.stopSpinner(ctx.t('npmInstalled', { skill }));
|
|
74
85
|
} else {
|
|
@@ -84,12 +95,16 @@ function setupNode(ctx, skillsDir) {
|
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
97
|
|
|
87
|
-
// Chromium for chrome-devtools (puppeteer-based).
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
// Chromium for chrome-devtools (puppeteer-based, ~300MB).
|
|
99
|
+
// Uses stdio:inherit for native progress bar when downloading (TTY-only).
|
|
100
|
+
if (hasChromePkg) {
|
|
101
|
+
if (systemChrome) {
|
|
102
|
+
ctx.ui.info(ctx.t('chromiumSystemChromeFound', { path: systemChrome }));
|
|
103
|
+
} else {
|
|
104
|
+
ctx.ui.info(ctx.t('chromiumInstalling'));
|
|
105
|
+
const ok = dep.installChromium(chromeScripts, true);
|
|
106
|
+
ctx.ui.info(ok ? '✓ ' + ctx.t('chromiumReady') : ctx.t('chromiumSkipped'));
|
|
107
|
+
}
|
|
93
108
|
}
|
|
94
109
|
}
|
|
95
110
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haposoft/cafekit",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.8",
|
|
4
4
|
"description": "Claude Code-first spec-driven workflow for AI coding assistants. Bundles CafeKit hapo: skills, runtime hooks, agents, and installer scaffolding.",
|
|
5
5
|
"author": "Haposoft <nghialt@haposoft.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -191,6 +191,39 @@ node "$SKILL_DIR/navigate.js" --url https://example.com
|
|
|
191
191
|
|
|
192
192
|
**Linux/WSL only**: Run `"$SKILL_DIR/install-deps.sh"` first for Chrome system libraries.
|
|
193
193
|
|
|
194
|
+
## Chrome/Chromium Configuration
|
|
195
|
+
|
|
196
|
+
By default, chrome-devtools bundles Chromium via puppeteer (~300MB download on first install). To use your existing Chrome installation instead:
|
|
197
|
+
|
|
198
|
+
### Auto-Detection
|
|
199
|
+
|
|
200
|
+
chrome-devtools auto-detects Chrome at standard OS paths and uses it automatically:
|
|
201
|
+
|
|
202
|
+
| OS | Checked Paths |
|
|
203
|
+
|----|---------------|
|
|
204
|
+
| macOS | `/Applications/Google Chrome.app/...`, `/Applications/Chromium.app/...` |
|
|
205
|
+
| Windows | `%PROGRAMFILES%\Google\Chrome\...`, `%PROGRAMFILES(X86)%\...`, `%LOCALAPPDATA%\...` |
|
|
206
|
+
| Linux | `/usr/bin/google-chrome*`, `/usr/bin/chromium*`, `/snap/bin/chromium` |
|
|
207
|
+
|
|
208
|
+
### Manual Configuration
|
|
209
|
+
|
|
210
|
+
To force a specific Chrome installation, copy `.env.example` to `.env` and set `CHROME_EXECUTABLE_PATH`:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
cp "$SKILL_DIR/.env.example" "$SKILL_DIR/.env"
|
|
214
|
+
# Edit .env and set CHROME_EXECUTABLE_PATH to your Chrome executable
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Skip Chromium Download
|
|
218
|
+
|
|
219
|
+
To skip the ~300MB download during `npm install` when Chrome is already available:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
PUPPETEER_SKIP_DOWNLOAD=true npm install --prefix "$SKILL_DIR"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
If no system Chrome is found, puppeteer's bundled Chromium is used as a fallback.
|
|
226
|
+
|
|
194
227
|
## Session Persistence
|
|
195
228
|
|
|
196
229
|
Browser state persists across script executions via WebSocket endpoint file (`.browser-session.json`).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Chrome/Chromium Configuration
|
|
2
|
+
# ============================================================================
|
|
3
|
+
|
|
4
|
+
# Custom Chrome/Chromium executable path (optional)
|
|
5
|
+
# chrome-devtools auto-detects system Chrome at standard OS paths.
|
|
6
|
+
# Set this only if Chrome is installed in a non-standard location.
|
|
7
|
+
#
|
|
8
|
+
# macOS:
|
|
9
|
+
# CHROME_EXECUTABLE_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome
|
|
10
|
+
#
|
|
11
|
+
# Windows:
|
|
12
|
+
# CHROME_EXECUTABLE_PATH=C:\Program Files\Google\Chrome\Application\chrome.exe
|
|
13
|
+
#
|
|
14
|
+
# Linux:
|
|
15
|
+
# CHROME_EXECUTABLE_PATH=/usr/bin/google-chrome
|
|
16
|
+
|
|
17
|
+
# Skip puppeteer's Chromium download during npm install (optional)
|
|
18
|
+
# Set to true if you already have Chrome/Chromium installed and want to skip
|
|
19
|
+
# the ~300MB Chromium download during `npm install`.
|
|
20
|
+
# PUPPETEER_SKIP_DOWNLOAD=true
|
|
@@ -48,6 +48,63 @@ export function resolveHeadless(value) {
|
|
|
48
48
|
return false;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Resolve Chrome/Chromium executable path.
|
|
53
|
+
* Fallback chain: CHROME_EXECUTABLE_PATH → PUPPETEER_EXECUTABLE_PATH →
|
|
54
|
+
* OS auto-detect → undefined (puppeteer bundled Chromium).
|
|
55
|
+
* @returns {string|undefined}
|
|
56
|
+
*/
|
|
57
|
+
export function resolveExecutablePath() {
|
|
58
|
+
// 1. Env-var overrides (CafeKit + Puppeteer conventions)
|
|
59
|
+
for (const key of ['CHROME_EXECUTABLE_PATH', 'PUPPETEER_EXECUTABLE_PATH']) {
|
|
60
|
+
const p = process.env[key];
|
|
61
|
+
if (!p) continue;
|
|
62
|
+
try {
|
|
63
|
+
if (fs.existsSync(p)) { log(`Chrome (${key}): ${p}`); return p; }
|
|
64
|
+
log(`${key} set but not found: ${p}`);
|
|
65
|
+
} catch { /* permission error — skip */ }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 2. OS auto-detection
|
|
69
|
+
const candidates = (() => {
|
|
70
|
+
switch (process.platform) {
|
|
71
|
+
case 'darwin':
|
|
72
|
+
return [
|
|
73
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
74
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
75
|
+
];
|
|
76
|
+
case 'win32': {
|
|
77
|
+
const pf = process.env.PROGRAMFILES;
|
|
78
|
+
const pf86 = process.env['PROGRAMFILES(X86)'];
|
|
79
|
+
const local = process.env.LOCALAPPDATA;
|
|
80
|
+
return [
|
|
81
|
+
pf && `${pf}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
82
|
+
pf86 && `${pf86}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
83
|
+
local && `${local}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
84
|
+
].filter(Boolean);
|
|
85
|
+
}
|
|
86
|
+
default: // Linux & others
|
|
87
|
+
return [
|
|
88
|
+
'/usr/bin/google-chrome-stable',
|
|
89
|
+
'/usr/bin/google-chrome',
|
|
90
|
+
'/usr/bin/chromium-browser',
|
|
91
|
+
'/usr/bin/chromium',
|
|
92
|
+
'/snap/bin/chromium',
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
})();
|
|
96
|
+
|
|
97
|
+
for (const p of candidates) {
|
|
98
|
+
try {
|
|
99
|
+
if (fs.existsSync(p)) { log(`Chrome auto-detected: ${p}`); return p; }
|
|
100
|
+
} catch { /* skip */ }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 3. Bundled Chromium fallback
|
|
104
|
+
log('No system Chrome found, using bundled Chromium');
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
51
108
|
/**
|
|
52
109
|
* Get default Chrome profile path based on OS
|
|
53
110
|
* @returns {string} - Path to Chrome's default user data directory
|
|
@@ -253,12 +310,16 @@ export async function getBrowser(options = {}) {
|
|
|
253
310
|
log(`Using default Chrome profile: ${userDataDir}`);
|
|
254
311
|
}
|
|
255
312
|
|
|
313
|
+
// Resolve Chrome executable — env var or OS auto-detect (fallback: bundled Chromium)
|
|
314
|
+
const executablePath = resolveExecutablePath();
|
|
315
|
+
|
|
256
316
|
// Destructure known properties — only pass Puppeteer-valid options to launch()
|
|
257
317
|
const { headless, args: extraArgs, viewport, useDefaultProfile, profile, browserUrl, wsEndpoint: _ws, userDataDir: _udd, ...restOptions } = options;
|
|
258
318
|
|
|
259
319
|
// Launch new browser
|
|
260
320
|
const launchOptions = {
|
|
261
321
|
headless: resolveHeadless(headless),
|
|
322
|
+
...(executablePath && { executablePath }),
|
|
262
323
|
args: [
|
|
263
324
|
'--no-sandbox',
|
|
264
325
|
'--disable-setuid-sandbox',
|