You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Unblocks cloud Claude agents (Claude Code on the Web) from developing on
the extension by adding a username/password login path that doesn't need
Google OAuth, plus a chrome-extension-tester-mcp config so the agent can
load the unpacked extension, drive the popup, and screenshot results.
The new email/password form in LoginView is build-flag gated by
ENABLE_PASSWORD_AUTH. Stable + dev release builds keep it off (form stays
hidden, real users continue to use OAuth); CCW + verify-job builds enable
it. The flow reuses the existing handleTokenLogin path so the JWT lands
in chrome.storage.sync["token"] indistinguishable from an OAuth token —
no downstream consumer (modular-rest client, profile store, translate
service, ConsoleCrane) sees the difference.
Coverage:
- tests/login-password.test.ts + login-password-disabled.test.ts —
Vitest, form rendering / validation / success+failure paths.
- tests/e2e/password-login.spec.ts — Playwright, end-to-end against
stubbed /user/login and /verify/token via the existing fixture.
The agent path uses only the MCP; tests/e2e/ stays the testing ground
and the two share no code. CLAUDE.md documents the one-time CCW setup
(network access, env vars, setup script) plus the curl + token-inject
sequence — including the gotcha that the password must be base64-encoded
in the /user/login body since modular-rest's client lib does this
internally and raw curl doesn't.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CLAUDE.md
+127-1Lines changed: 127 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -141,6 +141,7 @@ And the `SettingsObject` type in [src/common/types/messaging.ts](src/common/type
141
141
-**The mount root in Nibble must not block the page.** Set `width: 0; height: 0; position: fixed; top: 0; left: 0`. Children use their own `position: fixed` to position themselves relative to the viewport.
142
142
-**Theme dark class lives on `.subturtle-scope`, not `<html>`.** Tailwind's `dark:` rules are rewritten by `postcss-prefix-selector` to `.subturtle-scope.dark ...` — so the same element must carry both classes. The settings store handles this and a `MutationObserver` keeps Vue Teleport subtrees in sync.
143
143
-**`src/stores/profile.ts` imports types from a sibling repo.** The path `../../../dashboard-app/frontend/types/database.type` resolves to a directory _next to_ this repo's root, not inside it. The actual repo is [`codebridger/subturtle-dashboard-app`](https://github.com/codebridger/subturtle-dashboard-app); local builds work because devs check both repos out side-by-side. CI clones the dashboard repo into `../dashboard-app/` before `yarn build` runs (see [.github/workflows/release.yml](.github/workflows/release.yml)). Don't try to "fix" the import to a relative-internal path or vendor the file — both will drift.
144
+
-**Playwright Chromium download isn't on CCW's Trusted allowlist.** The chrome-extension-tester-mcp's `postinstall` runs `playwright install chromium`, which pulls from `cdn.playwright.dev` / `playwright.download.prss.microsoft.com`. CCW environments must use Custom network access with those hosts added — see [§ Cloud agent workflow](#cloud-agent-workflow-claude-code-on-the-web). The setup script caches Chromium into the VM snapshot so per-session cost is zero.
nibble-flow.spec.ts # content script mounting + Persian emitOpen
321
324
console-crane-lifecycle.spec.ts # modal stays open while Nibble toggles off
322
325
translate-flow.spec.ts # full Persian translate-and-save with page.route stubs
326
+
password-login.spec.ts # popup password form end-to-end with stubbed /user/login
323
327
visual-scale.spec.ts # rem→px rewrite regression net
324
328
```
325
329
326
330
### Test totals
327
331
328
-
79 unit / component tests across 9 files; 11 E2E specs across 5 files. Full suite runs in ~15s once Playwright's Chromium is warm.
332
+
138 unit / component tests across 19 files; 16 E2E specs across 6 files. Full suite runs in ~20s once Playwright's Chromium is warm.
333
+
334
+
## Cloud agent workflow (Claude Code on the Web)
335
+
336
+
Lets a cloud Claude agent on [Claude Code on the Web (CCW)](https://code.claude.com/docs/en/web-quickstart) clone the repo, build the extension, install it into a headless Chromium, log in with username/password against the live dev server at `https://dev.dashboard.subturtle.app/`, and drive popup + content scripts with screenshots — no Google OAuth, no local backend.
337
+
338
+
The agent path uses **only** the [chrome-extension-tester-mcp](https://github.com/BHUVAN-RJ/chrome-extension-testing-mcp) MCP server (declared in [.mcp.json](.mcp.json)). It is independent of the `tests/e2e/` Playwright fixture and shares no code with it; CI verify runs the spec, the agent runs the MCP.
339
+
340
+
### One-time CCW environment setup
341
+
342
+
Done once per CCW environment from [claude.ai/code](https://claude.ai/code) — these settings live in the cloud UI, not the repo.
343
+
344
+
**Network access:**`Custom`, keeping the Trusted defaults plus Playwright's Chromium-download hosts. Without these, the setup script's `npm install -g chrome-extension-tester-mcp` hangs while pulling Chromium:
345
+
```
346
+
cdn.playwright.dev
347
+
playwright.download.prss.microsoft.com
348
+
```
349
+
350
+
**Environment variables** (`.env` format, no quotes):
`AGENT_EMAIL` / `AGENT_PASSWORD` are not consumed by the build — they exist so the agent's Bash step can reference `$AGENT_EMAIL` / `$AGENT_PASSWORD` without hardcoding into the prompt. They must match an account that exists on the dev server (the agent does not register; the human or dashboard team seeds the account).
367
+
368
+
**Setup script** (runs as root on Ubuntu 24.04, cached as a VM snapshot — first session ~5 min, subsequent ones reuse the cache):
369
+
```bash
370
+
#!/bin/bash
371
+
set -e
372
+
373
+
cd"${CLAUDE_PROJECT_DIR:-/workspace}"
374
+
375
+
# 1. Install extension deps.
376
+
yarn install --frozen-lockfile
377
+
378
+
# 2. Materialize .env.production from CCW env vars (dotenv-webpack's safe:true
379
+
# requires every key in .env.example to be present at build time).
# 4. Install the MCP globally so Playwright Chromium is downloaded once
396
+
# into the cached snapshot. npx in .mcp.json resolves to this install.
397
+
npm install -g chrome-extension-tester-mcp@^2.1
398
+
```
399
+
400
+
### Driving the extension via the MCP
401
+
402
+
Once the environment is set up and the agent session starts, the cached snapshot already has `dist/` built and Playwright Chromium installed. The full login + screenshot loop becomes:
403
+
404
+
```
405
+
# 1. Load the unpacked extension into headless Chromium.
406
+
load_extension({ extension_path: "$PWD/dist" })
407
+
408
+
# 2. Get a JWT from the dev server with the credentials in CCW env vars.
409
+
# modular-rest's authentication.login POSTs to /user/login.
410
+
# The password MUST be base64-encoded — modular-rest's client library
411
+
# does this internally, but raw curl has to pre-encode. Without it
412
+
# the server returns HTTP 412 {"status":"error","e":{}}.
# Expected: "Logged In Successfully!" view (LoginView.vue:57-68 v-else branch).
427
+
```
428
+
429
+
The MCP exposes 14 tools — others worth knowing about: `inspect_dom` (eval JS in a page), `monitor_network` (capture requests during navigation), `send_message_to_background` (drive `chrome.runtime.onMessage` listeners), `get_service_worker_logs` (read background SW console output), `run_assertion` (returns structured PASS/FAIL). Full reference: the [chrome-extension-tester-mcp README](https://github.com/BHUVAN-RJ/chrome-extension-testing-mcp).
430
+
431
+
### Why password auth exists in this build
432
+
433
+
`ENABLE_PASSWORD_AUTH` gates the email + password form in [src/popup/views/LoginView.vue](src/popup/views/LoginView.vue) at build time via `dotenv-webpack`. CCW builds set it true so the agent (or a human dev) can log in by typing credentials; stable + dev release builds in [.github/workflows/release.yml](.github/workflows/release.yml) set it false so production users see only Google OAuth. The agent's direct-API path doesn't need the UI, but the UI is what makes manual testing possible.
434
+
435
+
### How auth works under the hood
436
+
437
+
The agent's direct-API path POSTs `/user/login` (via `curl`) and gets a JWT. Injecting that JWT into `chrome.storage.sync["token"]` (via the MCP's `extension_storage` tool) lands it in the *same slot* the post-OAuth `StoreUserTokenMessage` path uses — see [src/background.ts:62](src/background.ts) for the read side. modular-rest's client, the profile store, the translate service, and ConsoleCrane all see no difference between an OAuth-issued token and a password-issued token.
438
+
439
+
### Local dev fallback (no CCW, no MCP)
440
+
441
+
```bash
442
+
echo"ENABLE_PASSWORD_AUTH=true">> .env.production # plus the other 8 keys
443
+
yarn build && yarn dev # webpack --watch
444
+
# Load dist/ at chrome://extensions, click the extension icon, use the form.
445
+
```
446
+
447
+
The popup form drives `authentication.login` from `@modular-rest/client`, hitting whatever `SUBTURTLE_API_URL` points at. No MCP, no Playwright — just the same UI a real user would see.
448
+
449
+
### Boundary
450
+
451
+
The agent path uses only the MCP. `tests/e2e/` is the testing ground for CI verify and stays untouched by agent tooling; the two never share code. If you need to add a new agent capability, route it through the MCP's tools or a new MCP — not through the Playwright fixture.
329
452
330
453
## Verification checklist
331
454
@@ -341,6 +464,7 @@ Automated:
341
464
- Per-host Nibble toggle persists and normalizes (`www.` strip, case fold, dedup). → [tests/settings-host.test.ts](tests/settings-host.test.ts).
342
465
- ConsoleCrane on Persian / CJK / emoji inputs throws no `InvalidCharacterError` from `btoa` — covered at the encode level, the bridge level, and the full select-and-save flow. → [tests/route-params.test.ts](tests/route-params.test.ts), [tests/e2e/nibble-flow.spec.ts](tests/e2e/nibble-flow.spec.ts), [tests/e2e/translate-flow.spec.ts](tests/e2e/translate-flow.spec.ts).
343
466
- Visual scale is consistent on default-html-fontsize and 24px-html-fontsize hosts (postcss `rem→px` rewrite regression net). → [tests/e2e/visual-scale.spec.ts](tests/e2e/visual-scale.spec.ts).
0 commit comments