Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

11 Commits

Folders and files

Repository files navigation

Mini Headless

An independent, headless Mini Program simulator for macOS 26+ arm64. It exposes a compiled Mini Program dist to AI clients over MCP stdio without linking to the MiniSimulator app or its Swift runtime client. Both a dist/app.json layout and the common dist/miniprogram/app.json layout are accepted.

V1 surface

V1 is Skyline-first while supporting both providers: mini_headless_session_open defaults renderer to "skyline"; pass "webview" explicitly for PageFrame WebView. V1 intentionally exposes only:

  • persistent session open, close, state, and cross-MCP warm reattachment;
  • one-call bounded page observations spanning screenshot, document, Console, and Network domains;
  • source-informed event waits for visible text, exact runtime network callsites, document revisions, and provider frames;
  • PNG screenshots of the active provider surface, with pixel dimensions, viewport mapping scales, presentation readiness metadata, and retained MCP artifact resources;
  • coordinate and document-scoped node-ref tap, pointer, scroll, swipe, text, and key input;
  • navigateTo, redirectTo, reLaunch, switchTab, and navigateBack(delta);
  • semantic DOM tree and child-node reads;
  • coordinate hit testing and node box models;
  • inline, matched, and computed styles;
  • component data;
  • bounded, filter-before-limit Console and Network streams with event waits and per-request Network drill-down.

Elements uses the official Mini Program WARemoteDebug protocol for both providers. WebView is not modeled as raw Chromium CDP, and Console/Network are the AppService business streams. Arbitrary evaluate, mock, state mutation, performance, and security tools are outside V1.

Distribution and setup

The product is delivered as two artifacts:

  • a small Rslib JavaScript wrapper containing the MCP adapter, persistent daemon, user commands, and documentation;
  • a separate immutable Runtime Bundle containing Electron, native code, the complete Runtime Pack, and the complete built SessionWorker.

Maintainers assemble that Runtime Bundle from the MiniSimulator donor repository's Vendor/Electron.app, Vendor/RuntimePack, current built SessionWorker and RuntimeHost, and freshly built Mini Headless native binaries. End users install only a Mini Headless plugin. They do not install, launch, or depend on MiniSimulator.app.

Each plugin contains the same prebuilt Rslib wrapper and a release descriptor that pins one Runtime Bundle URL, byte length, opaque bundle ID, and explicit Runtime/ProjectOwner contract versions. The MCP stdio server becomes ready first, then downloads a missing Runtime Bundle in the background and atomically installs it under:

~/.mini-headless/runtimes/v8-<runtime-bundle-id>/runtime

Concurrent Codex, Claude Code, Cursor, and CodeBuddy adapters share the same install lock, selected Runtime Bundle, persistent daemon, and retained sessions. There is no required install, setup, credential import, or doctor command. setup <runtime-directory> remains an explicit maintainer override for a local bundle.

The 0.1.0-alpha.1 prerelease pins its immutable Runtime Bundle in runtime/release.json. Installation downloads that bundle on first use; it does not hash or revalidate Mini Headless-owned files.

Install the prerelease directly from the public repository marketplace:

codex plugin marketplace add Nightwalk2001/MiniHeadless
codex plugin add mini-headless@nightwalk

claude plugin marketplace add Nightwalk2001/MiniHeadless
claude plugin install mini-headless@nightwalk

codebuddy plugin marketplace add Nightwalk2001/MiniHeadless
codebuddy plugin install mini-headless@nightwalk

Cursor users install the same repository through the Cursor Marketplace, or load a checkout with bun run dev:cursor into ~/.cursor/plugins/local. Every host starts the bundled wrapper automatically; none requires a separate mcp add, Runtime setup, credential import, or doctor step.

See docs/usage.md for client configuration, the first session_open, observation revisions, warm-session behavior, and maintainer release ordering.

AI Agent operation loop

  1. Read the Mini Program source, then call mini_headless_session_open with an absolute compiled dist path and optional absolute sourceRoot. Keep its sessionId plus observation.navigationRevision.
  2. Use mini_headless_page_observe as the normal read path. It can return a visually settled screenshot, DOM tree, filtered Console entries, and filtered Network entries together. Its observation window states whether navigation remained stable across those domains.
  3. Use mini_headless_page_wait for the source-derived visible text or exact Network initiator.callsiteId that proves the intended state. Use the specialized screenshot, Elements, Console, Network, and network_get tools only for drill-down. Console and Network waits are event-driven; do not poll with fixed sleeps.
  4. Act with mini_headless_input or mini_headless_navigate, passing the latest expectedNavigationRevision. Both return a fresh observation; input also reports the observed navigation, document, frame, and visual-settlement effect.
  5. Call mini_headless_session_state only for diagnostics or recovery, not between normal operations.
  6. Call mini_headless_session_close to dispose the worker when finished. To reuse it instead, leave it open and disconnect the MCP adapter; the daemon detaches it and retains the warm session for five minutes.

STALE_TARGET on a mutation means its expectedNavigationRevision no longer names the active page. Observe again before deciding what to do. Read-only screenshot and Elements calls safely rebase once onto the active document; input is never automatically replayed. Navigation is never replayed after a renderer transition starts; the daemon may repeat one failed preflight only when the worker proves that it restored the original route before dispatch.

Screenshot coordinates

mini_headless_screenshot returns width and height in PNG pixels, the logical viewport, inputScaleX, and inputScaleY, plus stability and readiness (first provider frame, visual quietness, timeout, and animation state). Screenshots never guess business readiness from URLs. The PNG is available as a mini-headless://artifact/... resource for 30 minutes independently of session lifetime and is inlined only when inlineImage is true. Input actions and Elements nodeAt use viewport logical coordinates, not PNG pixel coordinates. Convert a point selected from the image before using it:

logicalX = imageX * inputScaleX
logicalY = imageY * inputScaleY

Both coordinate spaces use a top-left origin.

Process shape

AI client
  └─ short-lived Rslib MCP stdio adapter
       └─ versioned private current-user Unix socket, framed MSB1
            └─ persistent TypeScript daemon
                 ├─ one napi-rs ProjectOwner actor per canonical project
                 └─ one warm session
                      ├─ hidden Electron SessionWorker
                      │    ├─ commands: stdin, direct MSB1
                      │    ├─ events: fd 3, direct MSB1
                      │    ├─ official AppService + Skyline/PageFrame
                      │    └─ input + navigation + inspector streams
                      └─ Skyline/WebView native frame capture

The stdio adapter may come and go without terminating the daemon. Identical session specifications reattach to a warm session; a disconnected session is retained for five minutes, subject to a four-session LRU bound. Explicit close disposes the Electron worker. Mini Headless does not watch compiled dist for live rebuilds; a new session is required after the consumer regenerates output. The daemon owns ProjectOwner actors until daemon shutdown so catalog, compiler, and CAS state remain warm across MCP connections.

The adapter-to-daemon boundary is a versioned private UDS protocol with request/response correlation. The daemon does not wrap the real SessionWorker in a second per-operation RPC. It sends the worker's native command envelopes directly over stdin and consumes its native lifecycle, frame, inspector, Console, and Network events directly from fd 3; both streams use framed MSB1 MessagePack. The current private daemon protocol version is V5.

TypeScript owns MCP schemas, the persistent daemon, session lifetimes, Electron workers, semantic inspector routing, Console, and Network. Rust owns the project catalog, dependency graph, compilation scheduling, artifacts, CAS, and long-lived project state. Node calls Rust in-process through napi-rs; there is no Rust child-process or MSB1 compatibility boundary. C++ owns only the narrow runtime-native seams: the MiniSimulator NodeArena MNS1 decoder, stable Skyline shared-memory snapshots, the WebView IOSurface sender, and the daemon-side WebView IOSurface receiver/PNG capture.

The MiniSimulator app and this package intentionally evolve independently. Parent-repository sources are donor evidence for the Runtime Pack, never runtime imports.

Development

Requires Node.js 20 or newer, Bun, the Rust toolchain, the sibling ../MiniSimulator runtime-core checkout, and the sibling ../minicrab checkout for the general compiler and minicrab-common.

bun install
bun run check
bun run build:wrapper
bun run build:native

To exercise the current checkout as a real Codex plugin, use the complete local development flow:

bun run dev

It builds ProjectOwner against the current Rust ProjectCore, builds the current MiniSimulator SessionWorker, RuntimeHost, RuntimeResourceView and native adapters, assembles Runtime Bundle V8 under .codex-dev/runtime, creates a cache-busted local marketplace under .codex-dev, and installs mini-headless@mini-headless-dev. Start a new Codex task after the command finishes so the newly installed MCP tools are discovered.

To load the same checkout as a Cursor local plugin after that Runtime Bundle exists:

bun run dev:cursor

It rebuilds the wrapper, writes .cursor-dev/plugins/mini-headless with dist and skills symlinked for live wrapper iteration, and installs that folder at ~/.cursor/plugins/local/mini-headless. Reload the Cursor window so the MCP tools are discovered. Re-run bun run dev:cursor-prepare after wrapper changes if the plugin snapshot is stale; dist is a symlink, so a wrapper rebuild plus MCP reconnect is usually enough. Re-run bun run dev:runtime after native or SessionWorker changes.

Rslib emits dist/main.js and dist/daemon.js. tsgo is the TypeScript checker and Biome owns formatting and linting. TypeScript uses double quotes, omits semicolons, avoids unnecessary blocks, and prefers type over interface.

build:native uses the NAPI-RS v3 CLI for runtime/native/project-owner.darwin-arm64.node, then CMake and Node-API for runtime/native/inspector-node-store.node, runtime/native/skyline-snapshot.node, runtime/native/webview-snapshot.node, and runtime/native/webview-surface.node. Mini Headless owns the project-owner-napi adapter; the shared Rust engine follows the sibling minicrab Rust 2024, rustfmt, workspace-lint, and optimized-release policies. Provenance is recorded in docs/rust-provenance.md.

After build:native, assemble a complete Runtime Bundle from reviewed absolute inputs:

bun run prepare:runtime \
  --electron-app /absolute/path/to/MiniSimulator/Vendor/Electron.app \
  --runtime-pack /absolute/path/to/MiniSimulator/Vendor/RuntimePack \
  --session-worker /absolute/path/to/built/SessionWorker \
  --native-directory /absolute/path/to/MiniHeadless/runtime/native \
  --output /absolute/path/to/new/runtime

The same command prepares development and release bundles from already built inputs. It copies the current Vendor/runtime closure, writes the V8 manifest with an opaque build ID and explicit contract versions, and validates that the assembled closure is structurally complete. bun run dev:runtime performs the current-checkout build and assembly in one command. The full ordering is documented in docs/usage.md.

Runtime Bundle

The plugin does not embed the large runtime and never falls back to MiniSimulator.app or an installed WeChat DevTools application. Its pinned release descriptor lets the wrapper download this separately published Runtime Bundle on first use:

runtime/
  manifest.json
  electron/MiniHeadlessRuntime.app/
  Native/project-owner.darwin-arm64.node
  Native/inspector-node-store.node
  Native/runtime-resource-view.node
  Native/skyline-snapshot.node
  Native/webview-snapshot.node
  Native/webview-surface.node
  pack/                                  # complete Runtime Pack
  SessionWorker/                         # complete built SessionWorker
    main.js

The Runtime Bundle manifest uses schemaVersion: 8. ProjectOwner produces the current LaunchSnapshotRef, compile scopes, and ArtifactSlice DTOs consumed by the synchronously shipped SessionWorker contract. runtime/manifest.example.json documents that contract, but is not a runnable release manifest. prepare:runtime copies the Electron app and Runtime Pack including base libraries from the current Vendor tree, adds the built native binaries and prepared worker closure, and produces the complete input required by session_open.

The manifest carries an opaque runtimeBundleID plus explicit Runtime and ProjectOwner contract versions. Mini Headless does not hash or revalidate its own files. Downloads are bounded by the release descriptor's byte length; archive paths and the extracted structural closure are validated before atomic publication. An incomplete or manifest-invalid bundle fails before any child process starts.

The checked-in MiniSimulator Vendor/RuntimePack is the build donor, not a complete V8 Runtime Bundle by itself. Its files become publishable only as part of the self-contained closure produced by prepare:runtime.

Source and static builds do not prove that either provider can boot. Real Skyline and WebView evidence still requires a complete distributable Runtime Bundle and a compiled consumer dist.

The adapter version, daemon protocol version, runtime root, opaque bundle ID, and explicit contract versions select and verify the private daemon namespace.

For a source checkout, configure an MCP client with only the absolute wrapper entry:

{
  "mcpServers": {
    "mini-headless": {
      "command": "node",
      "args": ["/absolute/path/to/MiniHeadless/dist/main.js"]
    }
  }
}

Released plugins generate this configuration themselves. The installed selection is the no-environment default. MINI_HEADLESS_RUNTIME_ROOT is an explicit development override, not normal consumer setup.

The adapter writes MCP protocol data only to stdout. Daemon and child-process diagnostics go to stderr or the Mini Headless diagnostics directory.

Runtime credentials

When no explicit credential path is configured, every ticket refresh first imports the current MiniSimulator login from:

~/Library/Application Support/nightwalk.tencent.MiniSimulator/DevToolsAuth/login.json

If that login is unavailable or expired, the runtime tries its last Mini Headless-owned copy and then scans:

~/Library/Application Support/微信开发者工具

selects the newest unexpired login, and copies it to:

~/.mini-headless/auth/login.json

The user normally only needs to be logged in to MiniSimulator. WeChat DevTools discovery remains the final fallback. If no usable login is present, session_open asks the user to sign in and retry.

MINI_HEADLESS_CREDENTIALS_PATH is an advanced explicit override. When it is set, the runtime never scans or overwrites it; the file must be a regular, non-symlink file with mode 0600 and this bounded shape:

{
  "openid": "...",
  "signature": "...",
  "signatureExpiredTime": 1893456000000
}

auth import <login-json> and auth import-wechat remain available as manual account-selection and troubleshooting overrides. Both commands stop the matching persistent daemon before replacing credentials. doctor is likewise optional troubleshooting; it is not part of installation or first use.

See docs/architecture.md for ownership, protocols, identity, inspector, and release gates.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages