@kontextmind/kxm 0.7.132 → 0.7.133
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-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +2 -0
- package/docs/README.md +4 -3
- package/docs/adr/ADR-0002-browser-automation-steel-doks.md +1 -1
- package/docs/adr/ADR-0005-obscura-default-playwright.md +85 -0
- package/docs/adr/README.md +1 -0
- package/docs/contributing/ci-and-release.md +1 -0
- package/docs/contributing/development.md +1 -0
- package/docs/contributing/test-matrix.md +2 -0
- package/docs/guides/agent-skills.md +2 -2
- package/docs/guides/browser-automation.md +23 -7
- package/docs/kb/how-to-connect-playwright-to-obscura.md +69 -0
- package/docs/kb/how-to-connect-playwright-to-steel.md +5 -3
- package/docs/kb/why-automation-opened-different-browser.md +14 -11
- package/docs/prompts/browser-repro-fix.md +8 -8
- package/docs/reference/configuration.md +17 -1
- package/package.json +3 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +37 -0
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-browser-session/SKILL.md +4 -2
- package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +40 -30
- package/plugins/kxm/src/browser.ts +53 -2
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/scripts/obscura.mjs +424 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,8 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
+
- **Playwright uses Obscura by default.** `resolveBrowserCdpEndpoint()` returns `OBSCURA_CDP_URL` or `http://127.0.0.1:${OBSCURA_PORT:-9222}`. `KXM_BROWSER=steel` still returns the Steel session CDP URL. `node scripts/obscura.mjs` downloads pinned Obscura v0.2.3 and serves it with `--allow-private-network`. `npm run e2e` runs the Playwright smoke test in `test/e2e/` over CDP and does not run `playwright install`. Steel remains the path for human takeover, MFA, and the live session viewer. See [ADR-0005](docs/adr/ADR-0005-obscura-default-playwright.md) and [How do I connect Playwright to Obscura?](docs/kb/how-to-connect-playwright-to-obscura.md).
|
|
10
|
+
|
|
9
11
|
- **A live agent step uses a configurable one-shot timeout, and a cancelling run recovers when its child has already exited.**
|
|
10
12
|
The bound is the step `timeoutMs`, or the project `limits.agentStepTimeoutMs`
|
|
11
13
|
when the step omits it (minimum 60 seconds, default one hour). A wider step
|
package/docs/README.md
CHANGED
|
@@ -26,7 +26,7 @@ KXM connects coding agents through a durable, authenticated [hub](glossary.md#hu
|
|
|
26
26
|
| [Continuous improvement](guides/continuous-improvement.md) | Leads, workflow authors | Journals, retrospectives, improvement reports and coded-repeat candidates |
|
|
27
27
|
| [Governed skills](guides/governed-skills.md) | Operators, skill authors | The candidate, evaluation, promotion and rejection lifecycle |
|
|
28
28
|
| [Agent skills](guides/agent-skills.md) | Users of any harness | The bundled `SKILL.md` suite and how each harness loads it |
|
|
29
|
-
| [Browser automation](guides/browser-automation.md) | Developers, operators |
|
|
29
|
+
| [Browser automation](guides/browser-automation.md) | Developers, operators | Obscura for Playwright, Steel for takeover, safe credentials |
|
|
30
30
|
| [Nous providers](guides/nous-providers.md) | Pi operators | Opt-in Nous Portal models for Pi agents |
|
|
31
31
|
|
|
32
32
|
### Browser knowledge base
|
|
@@ -35,7 +35,8 @@ KXM connects coding agents through a durable, authenticated [hub](glossary.md#hu
|
|
|
35
35
|
|---|---|---|
|
|
36
36
|
| [How are credentials retrieved without exposing them to the model?](kb/how-credentials-retrieved-safely.md) | Browser operators | How agents log in without the model seeing secrets |
|
|
37
37
|
| [How do I capture a UI section and annotate changes for an agent?](kb/how-to-capture-and-annotate-section.md) | Browser operators | Send an agent a marked-up UI section to change |
|
|
38
|
-
| [How do I connect Playwright to
|
|
38
|
+
| [How do I connect Playwright to Obscura?](kb/how-to-connect-playwright-to-obscura.md) | Browser operators | Attach Playwright to the default Obscura browser |
|
|
39
|
+
| [How do I connect Playwright to the existing Steel session?](kb/how-to-connect-playwright-to-steel.md) | Browser operators | Attach Playwright to a Steel session for takeover |
|
|
39
40
|
| [How do I recover an expired session or remove an orphaned browser?](kb/how-to-recover-expired-session-or-orphan.md) | Browser operators | Restore a session or remove an orphaned browser |
|
|
40
41
|
| [How does an agent resume after MFA?](kb/how-to-resume-after-mfa.md) | Browser operators | Continue agent work after a person completes MFA |
|
|
41
42
|
| [How do I take over a browser session to log in?](kb/how-to-take-over-session.md) | Browser operators | Log in by hand inside an agent's browser session |
|
|
@@ -75,7 +76,7 @@ KXM connects coding agents through a durable, authenticated [hub](glossary.md#hu
|
|
|
75
76
|
| [Architecture](concepts/architecture.md) | Integrators, maintainers | The components, message and workflow lifecycles, and KXM's limits |
|
|
76
77
|
| [Trust model](concepts/trust-model.md) | Operators, security reviewers | Who holds which credential, project boundaries, and what provenance proves |
|
|
77
78
|
| [Data and storage](concepts/data-and-storage.md) | Operators, security reviewers | What each store holds, where it lives and how long it is kept |
|
|
78
|
-
| [Architecture decision records](adr/README.md) | Maintainers | The decision records: [browser automation](adr/ADR-0002-browser-automation-steel-doks.md), [SQLite-only store](adr/ADR-0003-sqlite-only-store.md), [edge identity](adr/ADR-0004-edge-identity-authentik.md) |
|
|
79
|
+
| [Architecture decision records](adr/README.md) | Maintainers | The decision records: [browser automation](adr/ADR-0002-browser-automation-steel-doks.md), [Obscura for Playwright](adr/ADR-0005-obscura-default-playwright.md), [SQLite-only store](adr/ADR-0003-sqlite-only-store.md), [edge identity](adr/ADR-0004-edge-identity-authentik.md) |
|
|
79
80
|
| [KXM contract package](contracts/README.md) | Maintainers, reviewers | The normative specifications for the local-first architecture, listed below |
|
|
80
81
|
|
|
81
82
|
### Contracts
|
|
@@ -12,7 +12,7 @@ authority: "decision"
|
|
|
12
12
|
confidence: "verified"
|
|
13
13
|
summary: "Adopt self-hosted Steel on DigitalOcean Kubernetes (DOKS) with agent-browser and Playwright as KXM's primary browser automation infrastructure."
|
|
14
14
|
tags: ["architecture", "decision", "browser", "steel", "doks", "playwright"]
|
|
15
|
-
related: ["docs/guides/browser-automation.md", "docs/guides/agent-skills.md"]
|
|
15
|
+
related: ["docs/guides/browser-automation.md", "docs/guides/agent-skills.md", "docs/adr/ADR-0005-obscura-default-playwright.md"]
|
|
16
16
|
details:
|
|
17
17
|
decision_drivers:
|
|
18
18
|
- "Eliminate per-minute SaaS browser provider costs"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema: "kxm.doc.v1"
|
|
3
|
+
id: "ADR-0005"
|
|
4
|
+
type: "adr"
|
|
5
|
+
title: "Obscura is the default browser for Playwright"
|
|
6
|
+
project: "kxm"
|
|
7
|
+
status: "accepted"
|
|
8
|
+
owner: "@operator"
|
|
9
|
+
created: "2026-09-27"
|
|
10
|
+
updated: "2026-09-27"
|
|
11
|
+
authority: "decision"
|
|
12
|
+
confidence: "verified"
|
|
13
|
+
summary: "Playwright testing and verification connect to pinned Obscura v0.2.3 over CDP. Steel stays the browser for human takeover, MFA, and the live session viewer."
|
|
14
|
+
tags: ["architecture", "decision", "browser", "obscura", "playwright", "cdp"]
|
|
15
|
+
related: ["docs/adr/ADR-0002-browser-automation-steel-doks.md", "docs/guides/browser-automation.md", "docs/kb/how-to-connect-playwright-to-obscura.md"]
|
|
16
|
+
details:
|
|
17
|
+
decision_drivers:
|
|
18
|
+
- "Playwright tests must run without a Steel cluster or a Playwright-managed browser download"
|
|
19
|
+
- "Local pages, including 127.0.0.1, must load in CI"
|
|
20
|
+
- "Human takeover, MFA, and the live session viewer stay on Steel"
|
|
21
|
+
supersedes: null
|
|
22
|
+
superseded_by: null
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# ADR-0005: Obscura is the default browser for Playwright
|
|
26
|
+
|
|
27
|
+
## Status
|
|
28
|
+
|
|
29
|
+
Accepted on 2026-09-27. This record does not supersede [ADR-0002](ADR-0002-browser-automation-steel-doks.md). Steel remains the browser for human takeover, MFA, and the live session viewer.
|
|
30
|
+
|
|
31
|
+
## Context
|
|
32
|
+
|
|
33
|
+
Playwright in this repository had no config, dependency, or CI job. Skills told agents to attach to a Steel session with `chromium.connectOverCDP(STEEL_CDP_URL)`. That makes every UI test depend on a remote Steel deployment, and it launches a local browser when the CDP URL is missing.
|
|
34
|
+
|
|
35
|
+
Obscura v0.2.3 is a headless Chromium build that speaks the Chrome DevTools Protocol on loopback. Playwright can attach with `chromium.connectOverCDP()`. It does not speak Playwright's own protocol (`chromium.connect`, `use.connectOptions`). Loading `127.0.0.1` requires `--allow-private-network`. Video recording is unsupported.
|
|
36
|
+
|
|
37
|
+
## Decision drivers
|
|
38
|
+
|
|
39
|
+
1. Playwright testing and verification need a default that runs on a developer machine and on `ubuntu-latest`.
|
|
40
|
+
2. A smoke test must assert a page served on `127.0.0.1`, so CI does not depend on the public internet.
|
|
41
|
+
3. Tests must not download Playwright's browser builds (`playwright install`).
|
|
42
|
+
4. Human takeover, MFA, and the live session viewer still need Steel's session API and viewer.
|
|
43
|
+
|
|
44
|
+
## Considered options
|
|
45
|
+
|
|
46
|
+
1. **Pinned Obscura over CDP, Steel only when `KXM_BROWSER=steel`.**
|
|
47
|
+
2. **Keep Steel as the Playwright default.**
|
|
48
|
+
3. **`playwright install` and `chromium.launch()` on the runner.**
|
|
49
|
+
|
|
50
|
+
### Option 1: pinned Obscura (chosen)
|
|
51
|
+
|
|
52
|
+
- Good, because the launcher downloads one pinned Apache-2.0 archive and the smoke test talks to loopback.
|
|
53
|
+
- Good, because `newContext()` still isolates pages, and screenshots work on the rendering build.
|
|
54
|
+
- Bad, because video recording and full `storageState` are unavailable.
|
|
55
|
+
- Bad, because Linux needs glibc 2.35 or newer, and a non-loopback bind needs `OBSCURA_CDP_TOKEN`.
|
|
56
|
+
|
|
57
|
+
### Option 2: Steel for every Playwright run (rejected)
|
|
58
|
+
|
|
59
|
+
- Good, because one CDP path covers tests and takeover.
|
|
60
|
+
- Bad, because tests then require a Steel deployment, an API key, and a network path that CI does not have.
|
|
61
|
+
|
|
62
|
+
### Option 3: Playwright's downloaded Chromium (rejected)
|
|
63
|
+
|
|
64
|
+
- Good, because `chromium.launch()` needs no second binary.
|
|
65
|
+
- Bad, because it is a second browser download, and it is the local browser the skills already tell agents to avoid.
|
|
66
|
+
|
|
67
|
+
## Decision
|
|
68
|
+
|
|
69
|
+
`resolveBrowserCdpEndpoint()` returns the Obscura CDP URL (`OBSCURA_CDP_URL`, or `http://127.0.0.1:${OBSCURA_PORT:-9222}`). `KXM_BROWSER=steel` returns the existing Steel session URL from `formatCDPEndpoint()`. Any other `KXM_BROWSER` value throws.
|
|
70
|
+
|
|
71
|
+
`scripts/obscura.mjs` downloads Obscura v0.2.3 for the current OS and architecture, checks the pinned sha256, and serves with `--allow-private-network`. `npm run e2e` ensures that server is ready and runs `playwright test`. The worker-scoped `browser` fixture connects with `chromium.connectOverCDP()`. `video` is `off`.
|
|
72
|
+
|
|
73
|
+
## Consequences
|
|
74
|
+
|
|
75
|
+
- Playwright tests and verification use Obscura unless the operator sets `KXM_BROWSER=steel`.
|
|
76
|
+
- Steel session create, release, takeover, and the session viewer are unchanged.
|
|
77
|
+
- CI for this smoke test is `.github/workflows/e2e.yml` on `ubuntu-latest`. It does not enable the paused CI, Nightly, or Real Pi smoke workflows.
|
|
78
|
+
- The `node --test` globs do not collect `test/e2e/`.
|
|
79
|
+
|
|
80
|
+
## Related
|
|
81
|
+
|
|
82
|
+
- [ADR-0002: Self-hosted Steel on DOKS](ADR-0002-browser-automation-steel-doks.md)
|
|
83
|
+
- [Browser automation](../guides/browser-automation.md)
|
|
84
|
+
- [How do I connect Playwright to Obscura?](../kb/how-to-connect-playwright-to-obscura.md)
|
|
85
|
+
- [Environment variables and limits](../reference/configuration.md#browser-automation)
|
package/docs/adr/README.md
CHANGED
|
@@ -10,6 +10,7 @@ An architecture decision record (ADR) captures one significant decision about KX
|
|
|
10
10
|
| [ADR-0002](ADR-0002-browser-automation-steel-doks.md) | Self-hosted Steel for reusable browser automation and human takeover | Accepted | 2026-09-14 |
|
|
11
11
|
| [ADR-0003](ADR-0003-sqlite-only-store.md) | SQLite as the only store | Accepted | 2026-09-17 |
|
|
12
12
|
| [ADR-0004](ADR-0004-edge-identity-authentik.md) | Edge identity with Authentik; the hub owns no browser identity | Accepted | 2026-09-20 |
|
|
13
|
+
| [ADR-0005](ADR-0005-obscura-default-playwright.md) | Obscura is the default browser for Playwright; Steel stays for takeover | Accepted | 2026-09-27 |
|
|
13
14
|
|
|
14
15
|
ADR-001 is the original decision record for the local Runtime. It lives with the contracts in [`docs/contracts/architecture.md`](../contracts/architecture.md) because it is the root of those contracts, and it keeps its original three-digit number. Records in this directory continue the sequence from 0002. There is no ADR-0001.
|
|
15
16
|
|
|
@@ -42,6 +42,7 @@ together.
|
|
|
42
42
|
| Merged pull request | `auto-release.yml` | Tags the merge commit and dispatches `release.yml` |
|
|
43
43
|
| Tag push or dispatch | `release.yml` | Verifies, packs and publishes (see [Release flow](#release-flow)) |
|
|
44
44
|
| Manual only | `smoke.yml` | Real Pi smoke, currently disabled (see [Smoke tests](#smoke-tests)) |
|
|
45
|
+
| Pull request, or manual | `e2e.yml` | `npm run e2e` on `ubuntu-latest`: Obscura v0.2.3 plus the Playwright smoke test. This workflow does not enable CI, Nightly, or Real Pi smoke |
|
|
45
46
|
|
|
46
47
|
The npm scripts behind those rows:
|
|
47
48
|
|
|
@@ -264,6 +264,7 @@ Other scripts you will use:
|
|
|
264
264
|
| `npm run test:coverage` | Core suite with coverage floors |
|
|
265
265
|
| `npm run test:coverage:complete` | Core plus simulations with the higher nightly floors |
|
|
266
266
|
| `npm run lint:docs` | Markdown lint only |
|
|
267
|
+
| `npm run e2e` | Start Obscura if needed, then Playwright in `test/e2e/` (`node --test` does not collect that directory) |
|
|
267
268
|
| `npm run validate:pr` | The three-minute CI gate; see [CI and release](ci-and-release.md) |
|
|
268
269
|
| `npm run validate:ci` | Coverage suite, `check` and a package dry run (not run by CI today) |
|
|
269
270
|
| `npm run validate:claude` | Strict Claude plugin and marketplace validation |
|
|
@@ -165,6 +165,8 @@ up or restores stores a running supervisor wrote.
|
|
|
165
165
|
| Release packs `kxm-<v>.tgz`, uploads to a draft fail-closed, proves the digest and never clobbers | `kxm-release-github.test.ts`, `ci-contract.test.ts` |
|
|
166
166
|
| npm publish requires a published release and a matching asset digest | `kxm-publish-npm.test.ts` |
|
|
167
167
|
| CI required jobs are unconditional; runner selector, coverage floors and release triggers are pinned | `ci-contract.test.ts` |
|
|
168
|
+
| Playwright CDP defaults to Obscura; `KXM_BROWSER=steel` uses the Steel session URL | `browser.test.ts` |
|
|
169
|
+
| Obscura Playwright smoke loads a local page over CDP | `test/e2e/obscura-smoke.spec.ts` via `npm run e2e` (not `node --test`) |
|
|
168
170
|
| Skill suite: manifest shape, one owner per command, strict YAML frontmatter, no legacy names, mirror parity | `skill-suite.test.ts` ("every bundled SKILL.md frontmatter parses as strict YAML") |
|
|
169
171
|
| The extension and MCP server never import the hub, store or workflow; library bundles stay host-neutral | `import-boundary.test.ts` |
|
|
170
172
|
| Workspace packages keep their layers, required files and by-name imports | `package-layers.test.ts` |
|
|
@@ -68,7 +68,7 @@ Tools map the same way: peer tools to `kxm-peer`, workflow tools to `kxm-workflo
|
|
|
68
68
|
|
|
69
69
|
## Browser automation skills
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
Playwright testing and verification use Obscura. The Steel skills cover human takeover, MFA, and the live session viewer. They own no `kxm` command. See [Browser automation](browser-automation.md), [ADR-0005](../adr/ADR-0005-obscura-default-playwright.md), and [ADR-0002](../adr/ADR-0002-browser-automation-steel-doks.md).
|
|
72
72
|
|
|
73
73
|
| Skill | Use it to |
|
|
74
74
|
|---|---|
|
|
@@ -76,7 +76,7 @@ These skills drive a remote Steel browser that you host. They own no `kxm` comma
|
|
|
76
76
|
| `kxm-browser-takeover` | Hand a session to a human for MFA, login, CAPTCHA or sensitive consent, then resume |
|
|
77
77
|
| `kxm-browser-auth` | Use stored credentials and authenticated browser profiles safely |
|
|
78
78
|
| `kxm-browser-explore` | Explore a site, inspect its DOM and map a user flow with `agent-browser` |
|
|
79
|
-
| `kxm-browser-verify` | Reproduce a UI bug, gather evidence and write a durable Playwright test |
|
|
79
|
+
| `kxm-browser-verify` | Reproduce a UI bug on Obscura, gather evidence and write a durable Playwright test |
|
|
80
80
|
| `kxm-browser-diagnostics` | Diagnose Steel connectivity, CDP errors and timeouts, and clean up orphaned sessions |
|
|
81
81
|
| `kxm-browser-annotate` | Capture page sections, attach structured annotations and hand the changes to an agent |
|
|
82
82
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Browser automation
|
|
2
2
|
|
|
3
|
-
Give agents a real browser without giving them your desktop
|
|
3
|
+
Give agents a real browser without giving them your desktop. Playwright testing and verification use [Obscura](https://github.com/h4ckf0r0day/obscura) by default. Steel remains for human takeover, MFA, and the live session viewer. Explore pages with `agent-browser`, and hand a Steel session to a person for login, MFA, or consent. This page is for developers and operators. The `browser` mode names this page as its context file (`kxm explain --mode browser` counts it), so it stays short and procedure-first.
|
|
4
4
|
|
|
5
5
|
## Before you begin
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
7
|
+
- For Playwright: Node, and `node scripts/obscura.mjs` (it downloads pinned Obscura v0.2.3). [ADR-0005](../adr/ADR-0005-obscura-default-playwright.md) records that default.
|
|
8
|
+
- For takeover: a Steel deployment you operate, reachable over HTTPS, and its API key. [ADR-0002](../adr/ADR-0002-browser-automation-steel-doks.md) describes the reference deployment on Kubernetes.
|
|
9
|
+
- `curl` and `jq`. Optionally `agent-browser` for exploration. Playwright tests use Obscura; do not run `playwright install`.
|
|
9
10
|
- A secret manager for the API key. The bundled skills use `pass-cli`.
|
|
10
11
|
- The `kxm-browser-*` skills from the plugin or Pi package. See [Agent skills](agent-skills.md#browser-automation-skills).
|
|
11
12
|
|
|
@@ -13,11 +14,23 @@ Give agents a real browser without giving them your desktop: KXM's browser skill
|
|
|
13
14
|
|
|
14
15
|
| Component | Role |
|
|
15
16
|
|---|---|
|
|
16
|
-
|
|
|
17
|
+
| Obscura | Default headless browser for Playwright (`chromium.connectOverCDP`) |
|
|
18
|
+
| Steel | Isolated Chromium sessions, a REST API, a CDP WebSocket, and a session viewer for takeover |
|
|
17
19
|
| `agent-browser` | Fast, token-efficient exploration: accessibility snapshots, navigation, DOM inspection |
|
|
18
20
|
| Playwright | Assertions, bug reproductions, visual proof and permanent regression tests |
|
|
19
21
|
| Secret manager | The only place the Steel API key and site credentials live |
|
|
20
|
-
| Human operator | Completes MFA, CAPTCHA, SSO or consent in the session viewer |
|
|
22
|
+
| Human operator | Completes MFA, CAPTCHA, SSO or consent in the Steel session viewer |
|
|
23
|
+
|
|
24
|
+
## Run Playwright on Obscura
|
|
25
|
+
|
|
26
|
+
`resolveBrowserCdpEndpoint()` returns the Obscura URL unless `KXM_BROWSER=steel`. The launcher and the settings are in [How do I connect Playwright to Obscura?](../kb/how-to-connect-playwright-to-obscura.md) and [Browser settings](../reference/configuration.md#browser-automation).
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
node scripts/obscura.mjs --ensure
|
|
30
|
+
npm run e2e
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Set `video: "off"` in Playwright. Obscura does not record video. Connect with the worker-scoped `browser` fixture and `chromium.connectOverCDP()`. `chromium.connect` and `use.connectOptions` are not supported.
|
|
21
34
|
|
|
22
35
|
## Configure the Steel endpoint
|
|
23
36
|
|
|
@@ -70,7 +83,7 @@ The CDP URL carries the API key in its query string. Treat it as a secret: never
|
|
|
70
83
|
echo "Session: $SESSION_ID"
|
|
71
84
|
```
|
|
72
85
|
|
|
73
|
-
3.
|
|
86
|
+
3. For exploration, attach `agent-browser --cdp "<cdp-url>"`. Playwright tests use Obscura. Set `KXM_BROWSER=steel` and `chromium.connectOverCDP(<cdp-url>)` only when the test must drive this takeover session. Check the session's state with `steel GET "/v1/sessions/$SESSION_ID"`.
|
|
74
87
|
4. For a quick fetch that needs no session, scrape instead:
|
|
75
88
|
|
|
76
89
|
```bash
|
|
@@ -128,7 +141,8 @@ The KXM browser library tracks these states in the process that owns the session
|
|
|
128
141
|
|---|---|---|
|
|
129
142
|
| `401` or `403` from Steel | Missing or wrong API key | Re-export `STEEL_API_KEY` from your secret manager |
|
|
130
143
|
| Requests go to an unexpected host | `STEEL_API_URL` is unset | Export it before starting the agent |
|
|
131
|
-
| Playwright opens a local browser | The client
|
|
144
|
+
| Playwright opens a local browser | The client called `chromium.launch()` or `chromium.connect()` | Use the worker-scoped fixture and `connectOverCDP` against Obscura |
|
|
145
|
+
| `Access to private/internal IP address` | Obscura was started without `--allow-private-network` | Run `node scripts/obscura.mjs`, which passes that flag |
|
|
132
146
|
| Signed-in state is gone | The session expired or was released | Create a new session and repeat the takeover |
|
|
133
147
|
| The viewer shows the page but clicks do nothing | The viewer is a screencast, and some capture modes do not forward clicks | Use the DevTools inspector at `$STEEL_API_URL/v1/devtools/inspector.html`, with the agent paused |
|
|
134
148
|
|
|
@@ -136,6 +150,7 @@ The KXM browser library tracks these states in the process that owns the session
|
|
|
136
150
|
|
|
137
151
|
- [How are credentials retrieved without exposing them to the model?](../kb/how-credentials-retrieved-safely.md)
|
|
138
152
|
- [How do I capture a UI section and annotate changes for an agent?](../kb/how-to-capture-and-annotate-section.md)
|
|
153
|
+
- [How do I connect Playwright to Obscura?](../kb/how-to-connect-playwright-to-obscura.md)
|
|
139
154
|
- [How do I connect Playwright to the existing Steel session?](../kb/how-to-connect-playwright-to-steel.md)
|
|
140
155
|
- [How do I recover an expired session or remove an orphaned browser?](../kb/how-to-recover-expired-session-or-orphan.md)
|
|
141
156
|
- [How does an agent resume after MFA?](../kb/how-to-resume-after-mfa.md)
|
|
@@ -156,5 +171,6 @@ The KXM browser library tracks these states in the process that owns the session
|
|
|
156
171
|
## Next steps
|
|
157
172
|
|
|
158
173
|
- The seven browser skills: [Agent skills](agent-skills.md#browser-automation-skills)
|
|
174
|
+
- Why Obscura is the Playwright default: [ADR-0005](../adr/ADR-0005-obscura-default-playwright.md)
|
|
159
175
|
- Why Steel, and the reference deployment: [ADR-0002](../adr/ADR-0002-browser-automation-steel-doks.md)
|
|
160
176
|
- Estimate the `browser` mode's prompt footprint: [`kxm explain`](../reference/cli-reference.md#kxm-explain)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema: "kxm.doc.v1"
|
|
3
|
+
id: "KB-BROWSER-010"
|
|
4
|
+
type: "kb"
|
|
5
|
+
title: "How do I connect Playwright to Obscura?"
|
|
6
|
+
project: "kxm"
|
|
7
|
+
status: "accepted"
|
|
8
|
+
owner: "@operator"
|
|
9
|
+
created: "2026-09-27"
|
|
10
|
+
updated: "2026-09-27"
|
|
11
|
+
authority: "instruction"
|
|
12
|
+
confidence: "verified"
|
|
13
|
+
summary: "Run Playwright against the pinned Obscura headless browser with chromium.connectOverCDP()."
|
|
14
|
+
tags: ["browser", "playwright", "cdp", "obscura", "testing"]
|
|
15
|
+
related: ["docs/guides/browser-automation.md", "docs/kb/how-to-connect-playwright-to-steel.md", "docs/adr/ADR-0005-obscura-default-playwright.md"]
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# How do I connect Playwright to Obscura?
|
|
19
|
+
|
|
20
|
+
Obscura is the default browser for Playwright testing and verification. Steel remains for human takeover, MFA, and the live session viewer (`KXM_BROWSER=steel`).
|
|
21
|
+
|
|
22
|
+
Obscura is [h4ckf0r0day/obscura](https://github.com/h4ckf0r0day/obscura) v0.2.3 (Apache-2.0). It speaks the Chrome DevTools Protocol. Playwright's own transport (`chromium.connect`, `use.connectOptions`) does not work. Override the worker-scoped `browser` fixture and call `chromium.connectOverCDP()`.
|
|
23
|
+
|
|
24
|
+
## 1. Start Obscura
|
|
25
|
+
|
|
26
|
+
From the repository root:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
node scripts/obscura.mjs --ensure
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The launcher downloads the pinned v0.2.3 archive for this OS and architecture into `.kxm/bin/` (kept out of Git) and starts `obscura serve --port ${OBSCURA_PORT:-9222} --allow-private-network`. `obscura` and `obscura-worker` stay in that directory together. `--allow-private-network` is required: without it, `page.goto("http://127.0.0.1/...")` fails with `Access to private/internal IP address`.
|
|
33
|
+
|
|
34
|
+
Readiness is HTTP GET `http://127.0.0.1:${OBSCURA_PORT:-9222}/json/version`. The process needs glibc 2.35 or newer on Linux. `--stop` stops the pidfile process. With no flag, the server stays in the foreground.
|
|
35
|
+
|
|
36
|
+
Do not run `playwright install`. Set `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` when you install npm dependencies so Playwright's postinstall does not download its own browsers.
|
|
37
|
+
|
|
38
|
+
## 2. Point Playwright at the CDP endpoint
|
|
39
|
+
|
|
40
|
+
`resolveObscuraCdpEndpoint()` returns `OBSCURA_CDP_URL`, or `http://127.0.0.1:${OBSCURA_PORT:-9222}` when that variable is unset. `ws://127.0.0.1:9222`, `ws://127.0.0.1:9222/devtools/browser`, and `http://127.0.0.1:9222` all connect. `resolveBrowserCdpEndpoint()` returns the same URL unless `KXM_BROWSER=steel`.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { test as base, chromium, type Browser } from "@playwright/test";
|
|
44
|
+
import { resolveObscuraCdpEndpoint } from "@kontextmind/kxm/runtime";
|
|
45
|
+
|
|
46
|
+
export const test = base.extend<{}, { browser: Browser }>({
|
|
47
|
+
browser: [async ({}, use) => {
|
|
48
|
+
const browser = await chromium.connectOverCDP(resolveObscuraCdpEndpoint());
|
|
49
|
+
await use(browser);
|
|
50
|
+
await browser.close();
|
|
51
|
+
}, { scope: "worker" }],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export { expect } from "@playwright/test";
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
In `playwright.config.ts`, set `workers: 1`, `video: "off"`, and `trace: "retain-on-failure"`. Video recording is unsupported. `newContext()` isolation works. `storageState` is limited. The browser timezone defaults to `Europe/Berlin`; set `OBSCURA_TIMEZONE` on the Obscura process to override it. KXM does not read `OBSCURA_TIMEZONE`. Obscura ignores `HTTP_PROXY` and `HTTPS_PROXY`.
|
|
58
|
+
|
|
59
|
+
A bind that is not loopback needs `OBSCURA_CDP_TOKEN` (at least 32 bytes) sent as an `Authorization: Bearer` header. The launcher binds `127.0.0.1` and does not set that token.
|
|
60
|
+
|
|
61
|
+
## 3. Run the smoke test
|
|
62
|
+
|
|
63
|
+
`npm run e2e` ensures Obscura is ready, then runs `playwright test`. The repository smoke test serves a page on `127.0.0.1` and asserts that page. It does not need the public internet.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm run e2e
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The `node --test` suite does not collect `test/e2e/`.
|
|
@@ -7,18 +7,20 @@ project: "kxm"
|
|
|
7
7
|
status: "accepted"
|
|
8
8
|
owner: "@operator"
|
|
9
9
|
created: "2026-09-14"
|
|
10
|
-
updated: "2026-09-
|
|
10
|
+
updated: "2026-09-27"
|
|
11
11
|
authority: "instruction"
|
|
12
12
|
confidence: "verified"
|
|
13
13
|
summary: "Attach Playwright to an active remote Steel browser session with chromium.connectOverCDP()."
|
|
14
14
|
tags: ["browser", "playwright", "cdp", "steel", "testing"]
|
|
15
|
-
related: ["docs/guides/browser-automation.md", "docs/kb/why-automation-opened-different-browser.md"]
|
|
15
|
+
related: ["docs/guides/browser-automation.md", "docs/kb/how-to-connect-playwright-to-obscura.md", "docs/kb/why-automation-opened-different-browser.md"]
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
# How do I connect Playwright to the existing Steel session?
|
|
19
19
|
|
|
20
|
+
Obscura is the default for Playwright testing and verification. Use this page when `KXM_BROWSER=steel` and you are attaching to a live Steel session for human takeover, MFA, or the session viewer. The default path is [How do I connect Playwright to Obscura?](how-to-connect-playwright-to-obscura.md).
|
|
21
|
+
|
|
20
22
|
Run Playwright against a remote Steel session instead of a local browser by
|
|
21
|
-
connecting over the Chrome DevTools Protocol (CDP).
|
|
23
|
+
connecting over the Chrome DevTools Protocol (CDP). `resolveBrowserCdpEndpoint(session)` returns the same URL as `formatCDPEndpoint()` when `KXM_BROWSER=steel`.
|
|
22
24
|
|
|
23
25
|
## 1. Build the CDP endpoint
|
|
24
26
|
|
|
@@ -7,19 +7,19 @@ project: "kxm"
|
|
|
7
7
|
status: "accepted"
|
|
8
8
|
owner: "@operator"
|
|
9
9
|
created: "2026-09-14"
|
|
10
|
-
updated: "2026-09-
|
|
10
|
+
updated: "2026-09-27"
|
|
11
11
|
authority: "instruction"
|
|
12
12
|
confidence: "verified"
|
|
13
13
|
summary: "Prevent accidental local browser launches and make Playwright and agent-browser attach to remote Steel."
|
|
14
14
|
tags: ["browser", "cdp", "playwright", "agent-browser", "troubleshooting"]
|
|
15
|
-
related: ["docs/guides/browser-automation.md", "docs/kb/how-to-connect-playwright-to-steel.md"]
|
|
15
|
+
related: ["docs/guides/browser-automation.md", "docs/kb/how-to-connect-playwright-to-obscura.md", "docs/kb/how-to-connect-playwright-to-steel.md"]
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
# Why did automation open a different browser?
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
Playwright testing connects to Obscura at `http://127.0.0.1:9222` unless `KXM_BROWSER=steel`. A local Chrome window means the client launched a browser instead of attaching over CDP. The Obscura steps are in [How do I connect Playwright to Obscura?](how-to-connect-playwright-to-obscura.md).
|
|
21
|
+
|
|
22
|
+
You expected automation to run on Obscura, or on a Steel takeover session, but a local Chrome window opened, or the agent's actions never appeared in the session you were watching.
|
|
23
23
|
|
|
24
24
|
## Causes
|
|
25
25
|
|
|
@@ -32,10 +32,13 @@ viewer.
|
|
|
32
32
|
browser.
|
|
33
33
|
- **Fix:** always pass the session's CDP URL:
|
|
34
34
|
`--cdp "wss://<steel-host>/v1/devtools?sessionId=<session-id>&apiKey=<steel-api-key>"`.
|
|
35
|
-
Build
|
|
35
|
+
Build the Obscura URL with `resolveObscuraCdpEndpoint()`, or the Steel URL with `formatCDPEndpoint()` when `KXM_BROWSER=steel`. See
|
|
36
|
+
[How do I connect Playwright to Obscura?](how-to-connect-playwright-to-obscura.md)
|
|
37
|
+
and
|
|
36
38
|
[How do I connect Playwright to the existing Steel session?](how-to-connect-playwright-to-steel.md).
|
|
37
|
-
3. **
|
|
38
|
-
- A script that falls back to
|
|
39
|
-
|
|
40
|
-
- **Fix:**
|
|
41
|
-
|
|
39
|
+
3. **The Playwright client had no CDP endpoint.**
|
|
40
|
+
- A script that falls back to `chromium.launch()` when no CDP URL is set
|
|
41
|
+
opens a local browser.
|
|
42
|
+
- **Fix:** use `resolveObscuraCdpEndpoint()` (default
|
|
43
|
+
`http://127.0.0.1:9222`). For a Steel takeover session, set
|
|
44
|
+
`KXM_BROWSER=steel` and load `STEEL_API_URL` and `STEEL_API_KEY`.
|
|
@@ -7,19 +7,19 @@ project: "kxm"
|
|
|
7
7
|
status: "accepted"
|
|
8
8
|
owner: "@operator"
|
|
9
9
|
created: "2026-09-14"
|
|
10
|
-
updated: "2026-09-
|
|
10
|
+
updated: "2026-09-27"
|
|
11
11
|
authority: "instruction"
|
|
12
12
|
confidence: "verified"
|
|
13
|
-
summary: "Reproduce a UI defect on
|
|
13
|
+
summary: "Reproduce a UI defect on Obscura, collect evidence, write a Playwright regression, and prove RED then GREEN."
|
|
14
14
|
tags: ["browser", "playwright", "repro", "prompt"]
|
|
15
|
-
related: ["docs/guides/browser-automation.md", "docs/kb/how-to-connect-playwright-to-steel.md"]
|
|
15
|
+
related: ["docs/guides/browser-automation.md", "docs/kb/how-to-connect-playwright-to-obscura.md", "docs/kb/how-to-connect-playwright-to-steel.md"]
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
# Task template: reproduce a UI bug and produce a Playwright regression test
|
|
19
19
|
|
|
20
20
|
## Purpose
|
|
21
21
|
|
|
22
|
-
Use this prompt to execute the full UI defect lifecycle: reproducing reported symptoms on
|
|
22
|
+
Use this prompt to execute the full UI defect lifecycle: reproducing reported symptoms on Obscura, collecting diagnostic evidence, writing a durable Playwright test, demonstrating failure before fix (RED), applying the code fix, and demonstrating success afterward (GREEN). Use a Steel session only for human takeover, MFA, or the live session viewer (`KXM_BROWSER=steel`).
|
|
23
23
|
|
|
24
24
|
## Canonical skill references
|
|
25
25
|
|
|
@@ -42,7 +42,7 @@ Use this prompt to execute the full UI defect lifecycle: reproducing reported sy
|
|
|
42
42
|
## Instructions for the agent
|
|
43
43
|
|
|
44
44
|
1. **Step 1: Reproduce**:
|
|
45
|
-
- Connect to
|
|
45
|
+
- Connect Playwright to Obscura with the worker-scoped `browser` fixture and walk the repro steps. Do not run `playwright install`.
|
|
46
46
|
- Confirm that actual behavior matches `{{ACTUAL_BEHAVIOR}}`.
|
|
47
47
|
|
|
48
48
|
2. **Step 2: Collect Diagnostic Evidence**:
|
|
@@ -53,13 +53,13 @@ Use this prompt to execute the full UI defect lifecycle: reproducing reported sy
|
|
|
53
53
|
- Use semantic locators (`getByRole`, `getByText`, `getByLabel`) rather than brittle XPath or dynamic classes.
|
|
54
54
|
|
|
55
55
|
4. **Step 4: Demonstrate Failure (RED)**:
|
|
56
|
-
- Run the test: `
|
|
56
|
+
- Run the test: `node scripts/obscura.mjs --ensure && playwright test {{TEST_FILE_PATH}}`.
|
|
57
57
|
- Verify the test fails cleanly with an assertion error that directly explains the defect.
|
|
58
58
|
|
|
59
59
|
5. **Step 5: Implement Code Fix**:
|
|
60
60
|
- Modify the source code to resolve the defect.
|
|
61
61
|
|
|
62
62
|
6. **Step 6: Demonstrate Success (GREEN)**:
|
|
63
|
-
- Re-run the Playwright test: `
|
|
63
|
+
- Re-run the Playwright test: `node scripts/obscura.mjs --ensure && playwright test {{TEST_FILE_PATH}}`.
|
|
64
64
|
- Capture clean verification output and screenshot `{{ARTIFACT_DIR}}/after.png`.
|
|
65
|
-
-
|
|
65
|
+
- When the run used a Steel takeover session, release that session. An Obscura run has no Steel session to release.
|
|
@@ -32,7 +32,7 @@ Names that start with `KXM_` are not all operator settings. This map covers ever
|
|
|
32
32
|
| Runtime supervisor | `KXM_STATE_HOME`, `KXM_RUNTIME_SYNC_INTERVAL_MS`, `KXM_RUNTIME_STOP_GRACE_MS` | [Runtime supervisor settings](#runtime-supervisor-settings) |
|
|
33
33
|
| Operator CLI and sessions | `KXM_USER_CONFIG_DIR`, `KXM_USER_TELEMETRY_DIR`, `KXM_SESSION_TOKEN`, `KXM_SESSION_BRIEF`, `KXM_WORKFLOW_*`, `GITHUB_TOKEN`, and others | [CLI and session settings](#cli-and-session-settings) |
|
|
34
34
|
| Nous model providers | `KXM_NOUS_PROVIDERS`, `KXM_NOUS_PROXY_URL`, `KXM_NOUS_DISCOVERY_TIMEOUT_MS`, `KXM_NOUS_CATALOG_FILE`, `NOUS_API_KEY` | [Nous providers](../guides/nous-providers.md) |
|
|
35
|
-
| Browser automation | `STEEL_API_URL`, `STEEL_API_KEY`, `STEEL_UI_URL`, `USE_PASS_CLI` | [Browser
|
|
35
|
+
| Browser automation | `KXM_BROWSER`, `OBSCURA_CDP_URL`, `OBSCURA_PORT`, `STEEL_API_URL`, `STEEL_API_KEY`, `STEEL_UI_URL`, `USE_PASS_CLI` | [Browser settings](#browser-automation) |
|
|
36
36
|
| Set by a harness, not by you | `KXM_PROJECT_DIR` (Claude Code plugin), `KXM_ATTEMPT_TOKEN` (Runtime attempts), `KXM_WORKER_IDENTITY_KEY`, `KXM_WORKER_GENERATION`, `KXM_WORKER_CHILD_INCARCATION`, `KXM_WORKER_SESSION_SCOPE` (worker supervisor to its Pi child) | [Internal variables](#internal-variables) |
|
|
37
37
|
| Maintainer and test only | `KXM_SMOKE*`, `KXM_ASSET*`, `KXM_RELEASE_TAG`, `KXM_PUBLISH_WAIT_MS`, `KXM_DETERMINISTIC_TEST_CLOCK`, `KXM_WORKER_STOP_AFTER_MS`, `KXM_STUDIO_ONCE` | [Development](../contributing/development.md) |
|
|
38
38
|
| Maintainer critic script | `KXM_CRITIC_DIR`, `KXM_REVIEW_TARGET` | [Maintainer critic script](#maintainer-critic-script) |
|
|
@@ -198,6 +198,22 @@ The Runtime supervisor runs `kxm run` workflows and syncs their summaries to the
|
|
|
198
198
|
| `KXM_ENTRY` | The running script | Entry point `kxm completion install` resolves the CLI directory from |
|
|
199
199
|
| `KXM_SESSION_ID` | Unset | Session ID stamped on gate command result envelopes |
|
|
200
200
|
|
|
201
|
+
## Browser automation
|
|
202
|
+
|
|
203
|
+
Playwright testing and verification use Obscura. Steel is for human takeover, MFA, and the live session viewer. `resolveBrowserCdpEndpoint()` in `plugins/kxm/src/browser.ts` chooses the CDP URL. `node scripts/obscura.mjs` downloads pinned Obscura v0.2.3 and serves it. See [Browser automation](../guides/browser-automation.md).
|
|
204
|
+
|
|
205
|
+
| Variable | Default | Effect |
|
|
206
|
+
|---|---|---|
|
|
207
|
+
| `KXM_BROWSER` | `obscura` | `obscura` selects the Obscura CDP URL. `steel` selects the Steel session URL from `formatCDPEndpoint()` and requires a session id. Any other value throws |
|
|
208
|
+
| `OBSCURA_CDP_URL` | `http://127.0.0.1:${OBSCURA_PORT:-9222}` | CDP URL passed to `chromium.connectOverCDP()`. When set, it wins over `OBSCURA_PORT` for that URL |
|
|
209
|
+
| `OBSCURA_PORT` | `9222` | TCP port for `obscura serve`. Used in the default CDP URL when `OBSCURA_CDP_URL` is unset. The launcher refuses to start when this port disagrees with the port in `OBSCURA_CDP_URL` |
|
|
210
|
+
| `STEEL_API_URL` | A KontextMind-operated deployment | Base URL of your Steel API. Set it before using `KXM_BROWSER=steel` |
|
|
211
|
+
| `STEEL_UI_URL` | `$STEEL_API_URL/ui` | Base URL of the Steel session viewer |
|
|
212
|
+
| `STEEL_API_KEY` | A `pass-cli` lookup | Steel API key. When unset, the library runs a `pass-cli` lookup of a fixed KontextMind vault item |
|
|
213
|
+
| `USE_PASS_CLI` | enabled | Set to `false` to disable that `pass-cli` fallback |
|
|
214
|
+
|
|
215
|
+
Obscura's own `OBSCURA_TIMEZONE` (default `Europe/Berlin`) and `OBSCURA_CDP_TOKEN` (required only for a non-loopback bind) are read by the Obscura process, not by KXM.
|
|
216
|
+
|
|
201
217
|
## Internal variables
|
|
202
218
|
|
|
203
219
|
KXM sets these for its own child processes. Do not set them yourself.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kontextmind/kxm",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.133",
|
|
4
4
|
"description": "KXM local-first multi-agent orchestration and operator dashboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "KontextMind",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"test:simulations": "npm run build && node scripts/run-bounded.mjs 1200000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/simulations/*.test.ts",
|
|
39
39
|
"test:complete": "npm run build && node scripts/run-bounded.mjs 2400000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/core/*.test.ts test/simulations/*.test.ts",
|
|
40
40
|
"test": "npm run build && node scripts/run-bounded.mjs 1200000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/core/*.test.ts packages/core/*/tests/unit/*.test.ts",
|
|
41
|
+
"e2e": "node scripts/obscura.mjs --ensure && playwright test",
|
|
41
42
|
"test:coverage:core": "npm run build && node scripts/run-bounded.mjs 2400000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 --experimental-test-coverage --test-coverage-lines=91 --test-coverage-branches=80 --test-coverage-functions=92 --test-coverage-include=plugins/kxm/src/**/*.ts --test-coverage-exclude=plugins/kxm/src/server.ts --test-coverage-exclude=plugins/kxm/src/mcp-server.ts --test-coverage-exclude=plugins/kxm/src/runtime-supervisor.ts --test-coverage-include=packages/core/*/src/**/*.ts test/core/*.test.ts packages/core/*/tests/unit/*.test.ts",
|
|
42
43
|
"test:coverage:complete": "npm run build && node scripts/run-bounded.mjs 2400000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 --experimental-test-coverage --test-coverage-lines=93 --test-coverage-branches=80 --test-coverage-functions=93 --test-coverage-include=plugins/kxm/src/**/*.ts --test-coverage-exclude=plugins/kxm/src/server.ts --test-coverage-exclude=plugins/kxm/src/mcp-server.ts --test-coverage-exclude=plugins/kxm/src/runtime-supervisor.ts --test-coverage-include=packages/core/*/src/**/*.ts test/core/*.test.ts test/simulations/*.test.ts packages/core/*/tests/unit/*.test.ts",
|
|
43
44
|
"test:coverage": "npm run test:coverage:core",
|
|
@@ -95,6 +96,7 @@
|
|
|
95
96
|
"devDependencies": {
|
|
96
97
|
"@earendil-works/pi-coding-agent": "0.86.1",
|
|
97
98
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
99
|
+
"@playwright/test": "1.63.0",
|
|
98
100
|
"@types/node": "26.6.2",
|
|
99
101
|
"ajv": "8.20.0",
|
|
100
102
|
"commander": "15.0.0",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "kxm",
|
|
4
4
|
"displayName": "KXM",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.133",
|
|
6
6
|
"description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "KontextMind",
|
|
@@ -17313,7 +17313,7 @@ function sessionTokenFixHint(policy) {
|
|
|
17313
17313
|
}
|
|
17314
17314
|
|
|
17315
17315
|
// plugins/kxm/src/mcp-server.ts
|
|
17316
|
-
var VERSION = "0.7.
|
|
17316
|
+
var VERSION = "0.7.133";
|
|
17317
17317
|
var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
17318
17318
|
var inbox = /* @__PURE__ */ new Map();
|
|
17319
17319
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
@@ -33383,6 +33383,40 @@ function formatCDPEndpoint(session, config) {
|
|
|
33383
33383
|
}
|
|
33384
33384
|
return `${wsProtocol}//${host}/v1/devtools?${searchParams.toString()}`;
|
|
33385
33385
|
}
|
|
33386
|
+
var DEFAULT_OBSCURA_CDP_URL = "http://127.0.0.1:9222";
|
|
33387
|
+
var DEFAULT_OBSCURA_PORT = 9222;
|
|
33388
|
+
function obscuraListenPort() {
|
|
33389
|
+
const raw = process.env.OBSCURA_PORT?.trim() ?? "";
|
|
33390
|
+
if (raw === "") return DEFAULT_OBSCURA_PORT;
|
|
33391
|
+
if (!/^[0-9]+$/.test(raw)) {
|
|
33392
|
+
throw new Error(`OBSCURA_PORT must be an integer from 1 to 65535 (received ${JSON.stringify(process.env.OBSCURA_PORT)})`);
|
|
33393
|
+
}
|
|
33394
|
+
const port = Number(raw);
|
|
33395
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
33396
|
+
throw new Error(`OBSCURA_PORT must be an integer from 1 to 65535 (received ${JSON.stringify(process.env.OBSCURA_PORT)})`);
|
|
33397
|
+
}
|
|
33398
|
+
return port;
|
|
33399
|
+
}
|
|
33400
|
+
function resolveObscuraCdpEndpoint() {
|
|
33401
|
+
const explicit = process.env.OBSCURA_CDP_URL?.trim() ?? "";
|
|
33402
|
+
if (explicit !== "") return explicit;
|
|
33403
|
+
const port = obscuraListenPort();
|
|
33404
|
+
if (port === DEFAULT_OBSCURA_PORT) return DEFAULT_OBSCURA_CDP_URL;
|
|
33405
|
+
return `http://127.0.0.1:${port}`;
|
|
33406
|
+
}
|
|
33407
|
+
function resolveBrowserCdpEndpoint(session, config) {
|
|
33408
|
+
const browser = (process.env.KXM_BROWSER ?? "").trim().toLowerCase();
|
|
33409
|
+
if (browser === "" || browser === "obscura") {
|
|
33410
|
+
return resolveObscuraCdpEndpoint();
|
|
33411
|
+
}
|
|
33412
|
+
if (browser === "steel") {
|
|
33413
|
+
if (!session?.id) {
|
|
33414
|
+
throw new Error("KXM_BROWSER=steel requires a Steel session id");
|
|
33415
|
+
}
|
|
33416
|
+
return formatCDPEndpoint(session, config ?? resolveSteelConfig());
|
|
33417
|
+
}
|
|
33418
|
+
throw new Error(`Unsupported KXM_BROWSER value ${JSON.stringify(process.env.KXM_BROWSER)}; expected "obscura" or "steel"`);
|
|
33419
|
+
}
|
|
33386
33420
|
function sanitizeLogOutput(input) {
|
|
33387
33421
|
if (typeof input === "string") {
|
|
33388
33422
|
return input.replace(/apiKey=[^&]+/g, "apiKey=[REDACTED]").replace(/steel_[a-f0-9]+/g, "steel_[REDACTED]");
|
|
@@ -34612,6 +34646,7 @@ export {
|
|
|
34612
34646
|
DEFAULT_LOG_MAX_BYTES,
|
|
34613
34647
|
DEFAULT_LOG_MAX_FILES,
|
|
34614
34648
|
DEFAULT_MODES_CONFIG,
|
|
34649
|
+
DEFAULT_OBSCURA_CDP_URL,
|
|
34615
34650
|
DEFAULT_RUNTIME_STOP_GRACE_MS,
|
|
34616
34651
|
DEFAULT_RUNTIME_SYNC_INTERVAL_MS,
|
|
34617
34652
|
DEFAULT_SOCKET_DIR,
|
|
@@ -34764,7 +34799,9 @@ export {
|
|
|
34764
34799
|
redactLogValue,
|
|
34765
34800
|
registerKxmRuntimeCloseHook,
|
|
34766
34801
|
resolveActiveMode,
|
|
34802
|
+
resolveBrowserCdpEndpoint,
|
|
34767
34803
|
resolveDispatchStatus,
|
|
34804
|
+
resolveObscuraCdpEndpoint,
|
|
34768
34805
|
resolvePassCliApiKey,
|
|
34769
34806
|
resolveSshHostG,
|
|
34770
34807
|
resolveSteelConfig,
|