browser-extension-manager 1.7.0 → 1.7.2
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/CLAUDE.md
CHANGED
|
@@ -43,6 +43,8 @@ To load the unpacked extension in Chrome: point chrome://extensions → "Load un
|
|
|
43
43
|
|
|
44
44
|
### For Framework Development (This Repository)
|
|
45
45
|
|
|
46
|
+
> **🚫 NEVER use `npx mgr ...` from the framework repo.** `npx mgr` is for CONSUMER projects only (where the bin is linked in `node_modules/.bin/`). From the framework repo, use `npm test`, `npm start`, etc. — the `scripts` in `package.json` call the local `bin/` directly. This applies to ALL four OMEGA frameworks (BEM/UJM/BXM/EM).
|
|
47
|
+
|
|
46
48
|
1. `npm install`
|
|
47
49
|
2. `npm start` — watch + compile `src/` → `dist/` via prepare-package
|
|
48
50
|
3. Test in the **designated test consumer** — `../powertools-browser-extension` is BXM's consumer for validating framework changes end-to-end (exercise any consumer-level flow there freely: builds, tests, packaging, runtime). From inside it, run `npx mgr install dev` to swap BXM to this local repo — required whenever you edit the framework source and want the consumer to pick up the changes (the consumer otherwise keeps its installed `node_modules/browser-extension-manager`). Reverse with `npx mgr install live`.
|
|
@@ -198,10 +200,11 @@ See [docs/cli.md](docs/cli.md).
|
|
|
198
200
|
|
|
199
201
|
## Development Workflow
|
|
200
202
|
|
|
201
|
-
- **🚫 NEVER
|
|
203
|
+
- **🚫 NEVER use `npx mgr ...` from the framework repo** — `npx mgr` is for CONSUMER projects only (where the bin lives in `node_modules/.bin/`). From the framework repo, use `npm test`, `npm start`, etc. — the `scripts` in `package.json` call `node bin/browser-extension-manager` directly. This applies to ALL four OMEGA frameworks (BEM/UJM/BXM/EM).
|
|
204
|
+
- **🚫 NEVER run `npm start`** (consumer projects) — it's the user's long-running dev watcher. Assume it's already running; if it isn't, **instruct the user to run it** rather than running it yourself (running it again kills theirs). To see output, **read the `logs/*.log` files** (`dev.log`, `build.log`, `test.log`) — never tail/attach to the process. Running `npx mgr test` is fine.
|
|
202
205
|
- **Where the output logs live:** the gulp pipeline tees all stdout/stderr to `<projectRoot>/logs/dev.log` (on `npm start`) or `logs/build.log` (on `npm run build`), truncated fresh each run, ANSI-stripped. `cat logs/dev.log` (or `grep` it) instead of scrolling scrollback. `npx mgr test` writes `logs/test.log`. See [docs/build-system.md](docs/build-system.md#log-files).
|
|
203
206
|
- **After editing files**, verify the gulp watcher recompiled successfully. Check for webpack/sass errors in the console output. A change that breaks the build is not a completed change.
|
|
204
|
-
- **Live-test
|
|
207
|
+
- **Live-test the extension via CDP.** Use the `chrome-devtools-extension` MCP upstream — it launches a per-session Chrome for Testing with the unpacked extension pre-loaded (`BXM_EXTENSION_PATH="$(pwd)/packaged/chromium/raw" claude`, then `router__enable_upstream`; stable Chrome ignores `--load-extension`). Plain web pages (no extension needed): the regular `chrome-devtools` MCP tools — your session auto-launches its own private Chrome on the first tool call. This is the primary way to confirm UI changes — type-checking and test suites verify code correctness, not feature correctness. See [docs/cdp-debugging.md](docs/cdp-debugging.md) + `~/.claude/mcp-server/servers/chrome-devtools-extension/CLAUDE.md`.
|
|
205
208
|
|
|
206
209
|
## Supply-Chain Security
|
|
207
210
|
|
|
@@ -230,6 +233,8 @@ Whenever you make a behavioral change (new command, new flag, new pattern, remov
|
|
|
230
233
|
|
|
231
234
|
Don't ship behavioral changes with stale docs. Validate first, then document — write docs that describe shipped reality, not intentions.
|
|
232
235
|
|
|
236
|
+
**The OMEGA docs are structurally MIRRORED.** This file's section skeleton, the consumer template (`src/defaults/CLAUDE.md`), shared-concept `docs/*.md` filenames, and the `omega:*` skills are identical in structure and order across the sister frameworks (UJM / BEM / BXM / EM / MAM — WM mirrors the library subset). Never add, rename, or reorder a section here without making the SAME change in every sister repo in the same pass. The canonical skeletons + omission rules live in the `omega:main` skill's `mirror-spec.md` resource.
|
|
237
|
+
|
|
233
238
|
## Documentation
|
|
234
239
|
|
|
235
240
|
API references for each subsystem live in `docs/`:
|
package/dist/defaults/CLAUDE.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# ========== Default Values ==========
|
|
2
2
|
# Browser Extension Manager (BXM) — consumer project
|
|
3
3
|
|
|
4
|
+
<!-- MAINTAINERS (framework repo): this consumer template is MIRRORED across UJM/BEM/BXM/EM/MAM — same sections, same order (framework-specific extras may be inserted; canonical sections are never reordered/renamed). Edit all five together. Canonical skeleton: omega:main skill → resources/mirror-spec.md -->
|
|
5
|
+
|
|
4
6
|
## Framework
|
|
5
7
|
|
|
6
8
|
This project consumes **Browser Extension Manager** (BXM) — a comprehensive framework for building modern cross-browser extensions (Chrome, Firefox, Edge, Opera, Brave). BXM provides one-line bootstrap per extension context, a component-based architecture (view + styles + script per context), a multi-browser build/release pipeline that produces store-uploadable zips, cross-context auth synchronization, and a built-in four-layer test framework.
|
package/dist/test/runner.js
CHANGED
|
@@ -402,6 +402,11 @@ function filterBySource(source, files, sourceFilter, pathPart) {
|
|
|
402
402
|
});
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
+
// Glob ignore patterns implementing the "underscore = not a suite" convention:
|
|
406
|
+
// `_`-prefixed FILES (e.g. test/_init.js) and everything under a `_`-prefixed
|
|
407
|
+
// DIRECTORY at any depth (helpers, fixtures — e.g. test/_helpers/harness.js).
|
|
408
|
+
const DISCOVERY_IGNORE = ['**/_*.js', '**/_*/**'];
|
|
409
|
+
|
|
405
410
|
function discoverTestFiles(target) {
|
|
406
411
|
const { source: sourceFilter, pathPart } = parseTarget(target);
|
|
407
412
|
const framework = [];
|
|
@@ -425,7 +430,7 @@ function discoverTestFiles(target) {
|
|
|
425
430
|
// Consumers write their own boot tests under <cwd>/test/boot/.
|
|
426
431
|
const frameworkSuitesDir = path.join(__dirname, 'suites');
|
|
427
432
|
if (jetpack.exists(frameworkSuitesDir)) {
|
|
428
|
-
const ignore = [
|
|
433
|
+
const ignore = [...DISCOVERY_IGNORE];
|
|
429
434
|
if (!isFrameworkSelfTest) ignore.push('boot/**');
|
|
430
435
|
glob('**/*.js', { cwd: frameworkSuitesDir, ignore }).sort().forEach((rel) => {
|
|
431
436
|
framework.push(path.join(frameworkSuitesDir, rel));
|
|
@@ -434,9 +439,10 @@ function discoverTestFiles(target) {
|
|
|
434
439
|
|
|
435
440
|
// Consumer project suites — CWD/test/**/*.js. Skip when running from inside the
|
|
436
441
|
// framework's own dist tree (where consumer-tests-dir === framework-tests-parent).
|
|
442
|
+
// Excludes `_`-prefixed files and directories (see DISCOVERY_IGNORE).
|
|
437
443
|
const projectTestsDir = path.join(process.cwd(), 'test');
|
|
438
444
|
if (jetpack.exists(projectTestsDir) && projectTestsDir !== path.dirname(frameworkSuitesDir)) {
|
|
439
|
-
glob('**/*.js', { cwd: projectTestsDir, ignore: [
|
|
445
|
+
glob('**/*.js', { cwd: projectTestsDir, ignore: [...DISCOVERY_IGNORE] }).sort().forEach((rel) => {
|
|
440
446
|
project.push(path.join(projectTestsDir, rel));
|
|
441
447
|
});
|
|
442
448
|
}
|
|
@@ -512,4 +518,4 @@ async function runInitSetups() {
|
|
|
512
518
|
}
|
|
513
519
|
}
|
|
514
520
|
|
|
515
|
-
module.exports = { run, SkipError };
|
|
521
|
+
module.exports = { run, SkipError, DISCOVERY_IGNORE };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Build-layer tests for test-suite discovery — specifically the
|
|
2
|
+
// "underscore = not a suite" convention: `_`-prefixed files and everything
|
|
3
|
+
// under `_`-prefixed directories (at ANY depth) must be excluded, so consumers
|
|
4
|
+
// can keep helpers and fixture trees (e.g. test/_helpers/harness.js)
|
|
5
|
+
// next to their suites.
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
type: 'suite',
|
|
11
|
+
layer: 'build',
|
|
12
|
+
description: 'test discovery — underscore exclusion convention',
|
|
13
|
+
tests: [
|
|
14
|
+
{
|
|
15
|
+
name: 'runner exports DISCOVERY_IGNORE',
|
|
16
|
+
run: (ctx) => {
|
|
17
|
+
const runner = require(path.join(__dirname, '..', '..', 'runner.js'));
|
|
18
|
+
ctx.expect(Array.isArray(runner.DISCOVERY_IGNORE)).toBe(true);
|
|
19
|
+
ctx.expect(runner.DISCOVERY_IGNORE.length >= 2).toBe(true);
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'glob with DISCOVERY_IGNORE skips _ files and _ dirs at any depth',
|
|
24
|
+
run: (ctx) => {
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const os = require('os');
|
|
27
|
+
const glob = require('glob').globSync;
|
|
28
|
+
const { DISCOVERY_IGNORE } = require(path.join(__dirname, '..', '..', 'runner.js'));
|
|
29
|
+
|
|
30
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'bxm-discovery-test-'));
|
|
31
|
+
const write = (rel) => {
|
|
32
|
+
const file = path.join(tmp, rel);
|
|
33
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
34
|
+
fs.writeFileSync(file, 'module.exports = {};\n');
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Should be DISCOVERED:
|
|
38
|
+
write('build/config.test.js');
|
|
39
|
+
write('page/runner.test.js');
|
|
40
|
+
write('boot/nested/deep.test.js');
|
|
41
|
+
|
|
42
|
+
// Should be EXCLUDED:
|
|
43
|
+
write('_init.js'); // top-level _ file
|
|
44
|
+
write('page/_helper.js'); // nested _ file
|
|
45
|
+
write('_helpers/harness.js'); // file in top-level _ dir
|
|
46
|
+
write('_fixtures/packages/runner/mod/index.js'); // deep inside a _ dir
|
|
47
|
+
write('boot/_private/util.js'); // _ dir below a layer dir
|
|
48
|
+
|
|
49
|
+
const found = glob('**/*.js', { cwd: tmp, ignore: [...DISCOVERY_IGNORE] }).sort();
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
ctx.expect(found).toEqual([
|
|
53
|
+
'boot/nested/deep.test.js',
|
|
54
|
+
'build/config.test.js',
|
|
55
|
+
'page/runner.test.js',
|
|
56
|
+
]);
|
|
57
|
+
} finally {
|
|
58
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
package/docs/cdp-debugging.md
CHANGED
|
@@ -1,45 +1,42 @@
|
|
|
1
1
|
# CDP Debugging (driving a live browser)
|
|
2
2
|
|
|
3
|
-
How to
|
|
3
|
+
How to drive a browser you can CONTROL — see the extension live, screenshot it, click, type, read console logs, inspect network requests — for agents (Claude via MCP/CDP) and humans. For BXM this is THE dev surface: the extension only exists inside a running browser.
|
|
4
4
|
|
|
5
|
-
> Mirrored across the
|
|
5
|
+
> Mirrored across the five sister frameworks (UJM / BEM / BXM / EM / WM) — same core section, framework-flavored. Edit all five together.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## The browser: your Claude session owns one
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
open -gna "Google Chrome" --args \
|
|
11
|
-
--remote-debugging-port=9223 \
|
|
12
|
-
--user-data-dir="$HOME/Library/Application Support/chrome-profiles/agent" \
|
|
13
|
-
--no-first-run --no-default-browser-check \
|
|
14
|
-
--disable-background-timer-throttling \
|
|
15
|
-
--disable-backgrounding-occluded-windows \
|
|
16
|
-
--disable-renderer-backgrounding
|
|
17
|
-
```
|
|
9
|
+
Browser work runs through the **`chrome-devtools` MCP** (via mcp-router). There is NO launch procedure anymore — no ports, no profile dirs, no curl checks:
|
|
18
10
|
|
|
19
|
-
|
|
11
|
+
- **Just call the tools** — `new_page`, `navigate_page`, `take_screenshot`, `click`, `fill`, `evaluate_script`, `list_console_messages`, `list_network_requests`. The browser auto-launches on the first call.
|
|
12
|
+
- **Each Claude session gets its OWN private Chrome** (`--isolated`): temp profile, CDP over an internal pipe. Parallel sessions cannot see or touch each other's pages — open and close pages freely, the whole browser is yours.
|
|
13
|
+
- **It dies with the session.** No orphans, no cleanup, nothing to kill.
|
|
14
|
+
- **Ephemeral profile** — cookies/logins do NOT persist between sessions. If a flow needs auth, log in during the task.
|
|
15
|
+
- **Self-signed HTTPS is pre-accepted** (`--acceptInsecureCerts` in the upstream) — dev servers load without certificate interstitials.
|
|
16
|
+
- **NEVER quit/kill Chrome by app name** (`killall "Google Chrome"`, osascript) — that's the user's personal browser, not yours.
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
Humans: the agent's Chrome window is visible — you can watch it drive. Full reference: `~/.claude/mcp-server/servers/chrome-devtools/CLAUDE.md`.
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
- **`--user-data-dir` is REQUIRED, not optional.** Chrome 136+ **silently ignores** `--remote-debugging-port` on the default profile — no error, no port, nothing (verified on Chrome 149). This is the #1 "why isn't CDP up" trap.
|
|
25
|
-
- **The profile dir IS the persistent state** — logins AND installed extensions. Cookies + localStorage survive relaunches (verified by round-trip), and so does an unpacked extension you've loaded (see below). Ecosystem convention: ONE shared profile at `~/Library/Application Support/chrome-profiles/agent` across all four frameworks, so setup is one-time.
|
|
26
|
-
- **One Chrome instance per profile dir — but MANY agents per instance.** CDP is multi-client (verified: two concurrent clients driving different tabs of one instance): agents and sessions attach to the SAME port, each drives its own tab, and all share the profile's logins and extensions. One agent per tab is the only rule. A second launch with the same dir just opens a window in the existing instance and **ignores the new debug port** — attach to the running one instead. Reach for a second profile + port (`…/b` on 9224) only for a different IDENTITY (a different account = a different cookie jar) or hard isolation.
|
|
27
|
-
- It runs **side-by-side with the daily Chrome** — a different `--user-data-dir` is a fully separate instance.
|
|
28
|
-
- **Quit by profile match, never by app name**: `pkill -f "chrome-profiles/agent"`. (`osascript 'tell app "Google Chrome" to quit'` hits the daily browser too — same app name.)
|
|
20
|
+
## Electron apps are the exception (attach, don't launch)
|
|
29
21
|
|
|
30
|
-
|
|
22
|
+
An Electron dev app is a running singleton — you ATTACH to it instead of launching a browser: the `chrome-devtools-electron` MCP upstream (reads `EM_CDP_PORT`, default 9222, expanded once at session start) or EM's per-invocation `npx mgr cdp`. See EM's `docs/cdp-debugging.md`.
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
- Instead, load it ONCE through the UI: in the agent-profile Chrome, open `chrome://extensions` → enable **Developer mode** → **Load unpacked** → select the build output. **The persistent profile keeps it installed across every relaunch** — reload it from `chrome://extensions` (or the ⟳ button) after rebuilds.
|
|
34
|
-
- The extension's surfaces appear as CDP targets: the background service worker (`type: "service_worker"`, `chrome-extension://<id>/…`), the popup while it's open, and content scripts inside the page targets they're injected into.
|
|
24
|
+
## BXM specifics: testing the extension (`chrome-devtools-extension` upstream)
|
|
35
25
|
|
|
36
|
-
|
|
26
|
+
The plain `chrome-devtools` browser has NO extension in it — and `--load-extension` is silently ignored by branded stable Chrome (removed ~v137; verified on 149). The fix is the dedicated **`chrome-devtools-extension` MCP upstream**: the same per-session isolated model, but it launches **Chrome for Testing** (which still honors `--load-extension` — verified on CfT 150) with your unpacked extension pre-loaded into the ephemeral profile.
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
|---|---|---|
|
|
40
|
-
| `chrome-devtools` MCP | rich interaction — click, fill, type, screenshots, network requests, console messages, performance traces | `CHROME_CDP_PORT` env var, **expanded ONCE when the Claude session spawns its MCP — set it BEFORE launching `claude`** (mid-session changes do nothing) |
|
|
41
|
-
| Any CDP client — including EM's `npx mgr cdp` run from any EM project | quick JS eval, per-renderer screenshots | per invocation: `EM_CDP_PORT=9223 npx mgr cdp eval "<url-substring>" 'document.title'` |
|
|
28
|
+
The recipe:
|
|
42
29
|
|
|
43
|
-
|
|
30
|
+
1. **Build the loadable output**: `npm run build` → `packaged/chromium/raw/` (strict-JSON manifest; `dist/` is NOT Chrome-loadable — its manifest is JSON5).
|
|
31
|
+
2. **Launch the session with the extension path** (env is expanded ONCE at session start — set it BEFORE `claude`):
|
|
32
|
+
```bash
|
|
33
|
+
BXM_EXTENSION_PATH="$(pwd)/packaged/chromium/raw" claude
|
|
34
|
+
```
|
|
35
|
+
3. **In the session, enable the upstream** (it's on-demand): `router__enable_upstream { name: "chrome-devtools-extension" }`. The first tool call launches CfT with the extension loaded. Tools are namespaced `chrome-devtools-extension__*` (34 tools — the base set + extension tools).
|
|
36
|
+
4. **Rebuild loop**: after `npm run build`, `router__disable_upstream` + `router__enable_upstream` → fresh browser with the rebuilt extension (the profile is ephemeral; the extension loads at launch).
|
|
44
37
|
|
|
45
|
-
|
|
38
|
+
What you can reach: the popup and extension pages as page targets, content scripts inside the pages they're injected into, and the background service worker (`evaluate_script` takes a `serviceWorkerId`). Chrome for Testing binaries live in `~/.cache/puppeteer/chrome/` (the upstream auto-resolves the newest; update with `npu npx @puppeteer/browsers install chrome@stable`).
|
|
39
|
+
|
|
40
|
+
Full recipe + gotchas: `~/.claude/mcp-server/servers/chrome-devtools-extension/CLAUDE.md`.
|
|
41
|
+
|
|
42
|
+
Navigating to a brand's UJM dev site? **`https://localhost:4000` — NEVER the LAN IP** (`https://192.168.x.x:...`). Port 4000 by default, increments (4001, …) when multiple sites run; exact port in `.temp/_config_browsersync.yml` at the WEBSITE project root (the UJM consumer, NOT this extension repo).
|
package/docs/test-framework.md
CHANGED
|
@@ -157,7 +157,7 @@ Consumers writing their own tests get this automatically when running through `n
|
|
|
157
157
|
- **Framework defaults**: `<BXM>/dist/test/suites/**/*.js`
|
|
158
158
|
- **Consumer suites**: `<cwd>/test/**/*.js`
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
**The underscore convention** (`DISCOVERY_IGNORE` in `src/test/runner.js`): `_`-prefixed FILES (`test/_init.js`, `test/page/_helper.js`) and everything under a `_`-prefixed DIRECTORY at **any depth** (`test/_fixtures/**`, `test/boot/_private/**`) are excluded from suite discovery. Put shared helpers, fixture data, and non-test support files in `_`-prefixed paths — e.g. `test/_fixtures/`, `test/_helpers/`. The runner still specifically loads `test/_init.js` as the lifecycle hook. Matches the same convention in BEM/EM/UJM. Files load alphabetically.
|
|
161
161
|
|
|
162
162
|
**Framework's boot suites are scoped to BXM self-test runs only.** When a consumer runs `npx bxm test`, the framework's `dist/test/suites/boot/**` is excluded from discovery (those tests assert on BXM's internal fixture extension). Consumers write their own boot tests under `<cwd>/test/boot/`. See [test-boot-layer.md](test-boot-layer.md).
|
|
163
163
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-extension-manager",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "Browser Extension Manager dependency manager",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"mgr": "bin/browser-extension-manager"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
|
-
"dist/",
|
|
38
37
|
"bin/",
|
|
38
|
+
"dist/",
|
|
39
39
|
"docs/",
|
|
40
40
|
"CLAUDE.md"
|
|
41
41
|
],
|