Deterministic DSL-first browser automation runtime driving Chrome over the DevTools Protocol (CDP) — for humans and LLM agents
Project description
ManulEngine
A deterministic automation runtime for both humans and LLM agents. Write (or generate) .hunt files in plain English; ManulEngine resolves every element with deterministic DOM heuristics and drives Chrome directly over the Chrome DevTools Protocol (CDP) — no Playwright, no selectors, no cloud APIs, no AI required.
The same runtime serves two drivers from one artifact:
- Humans author readable
.huntsteps (Click the 'Login' button) — QA tests, RPA, synthetic monitors. No selectors to maintain. - LLM agents drive it through JSON CLI commands (
manul map/run-step/read/schema) that target elements by human label, never CSS/XPath.
Built for agents — and it's measurably cheaper on tokens
An agent has to see a page before it can act. A browser driver like Playwright or Selenium doesn't help here — it gives code access to the page, not the model. An LLM agent built on one still has to serialize the page into the prompt, and the usual ways are page.content() (raw HTML) or the accessibility snapshot — both expensive. manul map instead emits a compact, landmark-grouped view of just the labelled, interactive elements. Measured with the GPT-4 tokenizer (cl100k_base) on representative pages:
| What an agent feeds the model to perceive a page | Tokens | vs manul map |
|---|---|---|
Raw HTML (page.content()) |
2,216 – 2,241 | 4–8× more |
| Accessibility tree (role + name) | 1,384 – 1,912 | 3.6–5× more |
manul map (compact JSON) |
278 – 528 | 1× |
So the perception step that every browser-agent pays on every turn costs ~4–8× fewer tokens with ManulEngine than dumping HTML, and ~3.6–5× fewer than the a11y tree. These are clean synthetic pages — real-world HTML is far more bloated, so the gap widens in practice. (Reproduce: python scripts/measure_tokens.py.)
Authoring is also leaner and far more durable: the same checkout flow written as a .hunt file is a touch smaller than the equivalent Playwright script (175 vs 204 tokens) but — the real point — carries zero CSS/XPath selectors to break when the markup shifts.
Status: Alpha. Solo-developed, actively battle-tested. Bugs are expected, APIs may evolve, and there are no promises about stability or production readiness. The core claim is transparency: when a step works, you can see exactly why; when it fails, you get the scoring breakdown to diagnose it.
📖 Full Documentation: Overview · Installation · Getting Started · DSL Syntax · DSL for LLMs · Reports & Explainability · Integration · Extensions · Loops & Pages
Syntax First
ManulEngine runs .hunt files — plain-English automation scripts that read like manual QA steps. Here is the DSL in action.
A complete flow
@context: Smoke test for a login page
@title: Login Smoke
@var: {email} = admin@example.com
@var: {password} = secret123
STEP 1: Open the app
NAVIGATE to https://example.com/login
VERIFY that 'Sign In' is present
STEP 2: Authenticate
FILL 'Email' field with '{email}'
VERIFY "Email" field has value "{email}"
FILL 'Password' field with '{password}'
CLICK the 'Sign In' button
VERIFY that 'Dashboard' is present
DONE.
Run it:
manul path/to/login.hunt
Every @var: is declared up front — never hardcode test data inside steps. VERIFY confirms state after every significant action. DONE. closes the mission.
Case-insensitive keywords. All DSL keywords are case-insensitive at runtime —
CLICK,Click, andclickall work. The canonical form used in documentation and generated files is ALL UPPERCASE.Element type hints are optional. Words like
button,link,field,dropdownplaced after the target outside quotes are not required, but they provide a strong heuristic signal that boosts scoring accuracy.CLICK the 'Login' buttonandCLICK the 'Login'both work — the former is more precise.
Conditional branching
Branch test logic with IF / ELIF / ELSE based on what the page actually contains. Nesting is supported.
STEP 1: Adaptive login
IF button 'SSO Login' exists:
CLICK the 'SSO Login' button
VERIFY that 'SSO Portal' is present
ELIF text 'Sign In' is present:
FILL 'Username' field with '{username}'
CLICK the 'Sign In' button
ELSE:
CLICK the 'Create Account' link
Conditions can check element existence, visible text, variable equality, substring containment, or simple truthiness — all evaluated against the live page.
Loops
Repeat actions with REPEAT, iterate data with FOR EACH, or poll dynamic state with WHILE. Loops nest freely with conditionals.
@var: {products} = Laptop, Headphones, Mouse
STEP 1: Add products to cart
FOR EACH {product} IN {products}:
FILL 'Search' field with '{product}'
PRESS Enter
CLICK the 'Add to Cart' button NEAR '{product}'
VERIFY that 'Added to cart' is present
STEP 2: Load all reviews
WHILE button 'Load More' exists:
CLICK the 'Load More' button
WAIT 2
STEP 3: Retry checkout
REPEAT 3 TIMES:
CLICK the 'Place Order' button
IF text 'Success' is present:
VERIFY that 'Order confirmed' is present
REPEAT N TIMES: runs a fixed count. FOR EACH {var} IN {collection}: iterates comma-separated values. WHILE <condition>: repeats until the condition is false (safety limit: 100 iterations). {i} counter is auto-set on every iteration.
Contextual navigation
When a page has repeating controls — multiple "Delete" buttons, "Edit" links in every row — use contextual qualifiers instead of brittle selectors.
CLICK the 'Edit' button NEAR 'John Doe'
CLICK the 'Login' button ON HEADER
CLICK the 'Privacy Policy' link ON FOOTER
CLICK the 'Delete' button INSIDE 'Actions' row with 'John Doe'
NEAR ranks by pixel distance. ON HEADER / ON FOOTER scopes to viewport zones. INSIDE restricts scoring to a resolved row or container subtree.
Variables, data-driven runs, and backend hooks
@var: {email} = admin@example.com
@var: {password} = secret123
@script: {db} = scripts.db_helpers
@data: users.csv
@tags: smoke, auth
[SETUP]
CALL PYTHON {db}.seed_user "{email}" "{password}"
[END SETUP]
STEP 1: Login
NAVIGATE to https://example.com/login
FILL 'Email' field with '{email}'
FILL 'Password' field with '{password}'
CLICK the 'Sign In' button
VERIFY that 'Dashboard' is present
STEP 2: Fetch and use an OTP
CLICK the 'Send OTP' button
CALL PYTHON api_helpers.fetch_otp "{email}" into {otp}
FILL 'OTP' field with '{otp}'
CLICK the 'Verify' button
VERIFY that 'Welcome' is present
[TEARDOWN]
CALL PYTHON {db}.clean_database "{email}"
[END TEARDOWN]
@data: loops the entire mission over each row in a CSV or JSON file. @tags: lets you filter runs with manul --tags smoke. [SETUP] / [TEARDOWN] run Python outside the browser for data seeding and cleanup. CALL PYTHON ... into {var} captures return values mid-test — ideal for OTPs, tokens, and backend state.
Explicit waits and strict assertions
WAIT FOR 'Submit' to be visible
WAIT FOR 'Loading...' to disappear
VERIFY "Email" field has value "{email}"
VERIFY "Save" button has text "Save Changes"
VERIFY "Search" input has placeholder "Type to search..."
Explicit waits poll element visibility over CDP instead of hardcoded sleeps. Strict assertions resolve the element through heuristics and compare exact text, value, or placeholder with ==.
WAIT FOR SELECTOR 'css' waits for a CSS selector to appear (useful for custom elements like ytd-video-renderer that have no stable visible text). The WAIT FOR 'target' TO BE VISIBLE form also accepts CSS selectors — if the quoted target looks like a CSS selector it routes to page.wait_for_selector() automatically.
Page scanning for LLM agents
FULL SCAN
FULL SCAN groups every interactive control on the page by its nearest semantic landmark (form, nav, header, dialog, section …) and prints a Markdown table per group. Designed for LLM-driven automation — an LLM can paste the output directly into its context window to decide which element to interact with next. Shadow DOM trees are traversed recursively, so controls inside custom elements (e.g. <ytd-*>, <mwc-*>) appear under a [shadow]-suffixed group.
Example output:
## Form: Login
| role | label | locator | tag | editable |
|------------|----------------------------------|--------------------------------------|----------|----------|
| textbox | Email | #email | input | yes |
| textbox | Password | #password | input | yes |
| button | Sign In | text=Sign In | button | no |
## Navigation
| role | label | locator | tag | editable |
|------------|----------------------------------|--------------------------------------|----------|----------|
| link | Home | text=Home | a | no |
| link | About | text=About | a | no |
SCAN PAGE remains available for generating draft .hunt files from a live page.
Agent commands — drive the engine from an external LLM
For agentic use, ManulEngine exposes a small set of JSON-emitting CLI commands (mirroring the Go sibling ManulEngine (Go)). They attach to an already-running Chrome over CDP, so an external model keeps one browser open and issues stateless calls against it. The JSON payload goes to stdout; all engine logs go to stderr, so a driver can pipe the output straight into a prompt.
# 1. start Chrome once with remote debugging
google-chrome --remote-debugging-port=9222 &
# 2. let the model see the page, act, and read — by human label, never CSS/XPath
manul schema # DSL grammar + agent JSON shapes (no browser)
manul map --tab example.com # compact landmark-grouped page map → JSON
manul run-step "Click the 'Login' button" # run one instruction → step-outcome JSON
manul read 'Order total' # read one labelled value → {value, found, reason}
manul read --selector '#cart' # sanitized region text → {text, selector}
Shared flags: --cdp <url> (default http://127.0.0.1:9222) and --tab <url-substr> to pick a tab. run-step returns a non-zero exit code when the step fails, and surfaces near candidates (with 0.0–1.0 scores) on a failed or low-confidence match so the agent can retarget without a re-scan. manul schema is the machine-readable contract a driver pins instead of stuffing full docs into every prompt.
Shared libraries and scheduling
@import: Login, Logout from lib/auth.hunt
@export: Checkout
@schedule: every 5 minutes
STEP 1: Setup
USE Login
STEP 2: Purchase flow
CLICK the 'Buy Now' button
VERIFY that 'Order Confirmed' is present
STEP 3: Cleanup
USE Logout
DONE.
@import: / USE reuses named STEP blocks across files. @export: controls visibility. @schedule: plus manul daemon turns any hunt into a recurring monitor or RPA job.
CLI
manul path/to/hunts/ # run all .hunt files in a directory
manul --headless path/to/file.hunt # headless single file
manul --tags smoke path/to/hunts/ # filter by tags
manul --html-report --screenshot on-fail path/ # reports + failure screenshots
manul --explain path/to/file.hunt # per-step scoring breakdown
manul --debug path/to/file.hunt # pause before every step
manul --retries 2 path/to/hunts/ # retry failed hunts
manul scan https://example.com # scan a page → draft.hunt
manul daemon path/to/hunts/ --headless # run scheduled hunts
Philosophy
Determinism, not prompt variance
The primary resolver is not an LLM. It is a weighted heuristic scorer (DOMScorer) backed by a native JavaScript TreeWalker. Scores are normalized on a 0.0–1.0 confidence scale across five channels: cache, semantics, text, attributes, and proximity. The result is repeatable: same page state plus same step text equals same resolution — no prompt variance, no cloud dependency.
When --explain is enabled, every resolved step prints a per-channel breakdown so you can see exactly which signals drove the decision and which lost:
┌─ EXPLAIN: Target = "Login"
│ Step: Click the 'Login' button
│
│ #1 <button> "Login"
│ total: 0.593
│ text: 0.281
│ attributes: 0.050
│ semantics: 0.225
│ proximity: 0.037
│ cache: 0.000
│
└─ Decision: selected "Login" with score 0.593
Native CDP, no Playwright
ManulEngine talks to the browser through its own Chrome DevTools Protocol client — a thin async WebSocket transport in manul_engine/cdp/ with a single runtime dependency (websockets). There is no Playwright, no Node.js, and no bundled browser download: the engine launches the Chrome/Chromium you already have on PATH (or attaches to a running one) and drives it directly.
Why own the protocol layer:
- One small dependency, fully inspectable. The whole browser driver is a handful of readable Python files (
conn.pytransport,chrome.pylauncher,page.pypage/frame/element model) rather than a large vendored toolchain. What the engine sends to Chrome is exactly what you can read. - Trusted input by default. Clicks and keystrokes are dispatched via CDP
Input.*events at real coordinates, and form values go through the native value setter so React/Vue/Angular state updates fire — noforcehacks needed. - Per-frame execution contexts. A selector is resolved once inside the owning frame's execution context, then every operation runs against that handle — so same-origin iframes (and OOPIF child targets) are first-class, not an afterthought.
- CDP is Chromium-only by design. Because the protocol is Chrome's, the engine drives Chrome/Chromium only. Firefox/WebKit are intentionally not supported; pick the concrete binary with
channel(chrome,msedge,chromium, …) orexecutable_path.
This is the same philosophy as the Go sibling ManulEngine (Go): plain-English .hunt files, deterministic heuristics, and a hand-written CDP layer instead of a heavyweight automation framework.
Parallel execution
Run a directory of hunts concurrently with --workers N (or the workers config key). Because of the GIL, parallelism uses subprocess workers — each runs in its own process with its own Chrome instance, fully isolated, and the reporter merges their results. (The Go sibling reaches the same goal with native goroutines and a WorkerPool.)
Dual-persona workflow
Manual QA writes plain-English .hunt steps — no code required. SDETs extend the same files with Python hooks ([SETUP] / [TEARDOWN], CALL PYTHON), lifecycle orchestration (@before_all / @after_all), and @custom_control handlers for complex widgets. Both personas work on the same artifact.
No AI in the loop — fully deterministic
ManulEngine has no LLM inside it. Element resolution is 100% the deterministic DOMScorer — same page state + same step ⇒ same result, every run, with no model to install, no temperature to pin, and no network calls. The intelligence lives in the external agent that drives the engine via the agent commands (map / run-step / read / schema); the runtime itself stays a predictable execution layer. (An optional in-process Ollama fallback existed in early development — it was removed in favour of this clean split.)
Four Automation Pillars
The same runtime and the same DSL serve four use cases:
| Pillar | How |
|---|---|
| QA / E2E testing | Write plain-English flows, verify outcomes, attach HTML reports and screenshots. No selectors in the test source. |
| RPA workflows | Log into portals, fill forms, extract values, hand off to Python for backend or filesystem steps. |
| Synthetic monitoring | Pair .hunt files with @schedule: and manul daemon for recurring health checks. |
| AI agent targets | Constrained DSL execution is safer than raw CDP/scripting for external agents — the runtime still owns scoring, retries, and validation. |
Key Features
- Conditional branching & loops —
IF/ELIF/ELSEfor adaptive flows;REPEAT,FOR EACH,WHILEfor iterating data, retrying actions, and polling dynamic state. Full nesting support. - Explainability —
--explainprints per-channel scoring breakdowns on the CLI. The VS Code extension shows hover tooltips and a title-bar "Explain Current Step" action during debug pauses. - What-If Analysis REPL — During a
--debugpause, typewto enter an interactive REPL that evaluates hypothetical steps against the live DOM without executing them. Returns a 0–10 confidence score, risk assessment, and highlights the best match on the page. - Desktop / Electron — Set
executable_pathin the config and useOPEN APPinstead ofNAVIGATEto drive Electron apps with the same DSL. - Python API (
ManulSession) — Async context manager for pure-Python automation. Routes every call through the full heuristic pipeline.from manul_engine import ManulSession async with ManulSession(headless=True) as session: await session.navigate("https://example.com/login") await session.fill("Username field", "admin") await session.click("Log in button") await session.verify("Welcome")
- Smart recorder — Captures semantic intent (e.g.,
Select 'Option' from 'Dropdown') instead of brittle pointer events. - Custom controls —
@custom_control(page, target)decorator lets SDETs handle complex widgets (datepickers, virtual tables, canvas elements) with raw CDP while the hunt file keeps a single readable step. Handlers receive a typedControlContext(ctx.page/ctx.action/ctx.value/ctx.target/ctx.page_name/ctx.url/ctx.step);manul controls listshows the registry; misses against a sibling page print a one-line hint. - Lifecycle hooks —
@before_all,@after_all,@before_group,@after_groupinmanul_hooks.pyfor suite-wide setup and teardown. - HTML reports —
--html-reportgenerates a self-contained dark-themed report with accordions, screenshots, tag filters, and run-session merging across CLI invocations. - Docker CI runner —
ghcr.io/alexbeatnik/manul-engine:0.1.0runs headless in CI withdumb-init, non-root user, andMANUL_*env overrides.
Architecture
manul_engine/
__init__.py public API (ManulEngine, ManulSession, custom_control, …)
api.py ManulSession — high-level async context manager (Python-only users)
core.py ManulEngine — the .hunt DSL runner; targeting pipeline + orchestration
actions.py click / fill / select / hover / drag executors
debug.py interactive debug pause + extension-protocol handshake
helpers.py pure functions: parse_hunt_blocks, classify_step, substitute_memory, …
hooks.py @before_all / @after_group lifecycle + CALL PYTHON
imports.py @import / USE resolution for .hunt libraries
controls.py @custom_control registry (page-scoped overrides)
scoring.py deterministic DOMScorer (cache · semantics · text · attributes · proximity)
agent_cli.py agent-facing CLI: schema / map / read / run-step (JSON for LLM drivers)
scanner.py DOM snapshot → draft .hunt (manul scan <URL>)
recorder.py interactive recorder (manul record <URL>)
reporter.py HTML + JSON test reports
scheduler.py daemon mode (@schedule:)
cdp/ native Chrome DevTools Protocol backend (conn / chrome / page /
browser / protocol / keys) — replaces Playwright
variables.py ScopedVariables (5 levels: row > step > mission > global > import)
prompts.py global config (THRESHOLDS, BROWSER_ARGS, …)
config.py EngineConfig dataclass (injectable)
cli.py manul CLI entry point
See docs/overview.md for the deep-dive architecture walkthrough.
Quickstart
Install
pip install manul-engine==0.1.0
# Requires a system-installed Google Chrome / Chromium on PATH.
Configure
Create manul_engine_configuration.json in the workspace root. All keys are optional:
{
"browser": "chromium",
"semantic_cache_enabled": true
}
This is the minimal recommended config — fully heuristics-only, no AI dependency.
Run
manul tests/login.hunt # single file
manul tests/ # all hunts in a directory
manul --headless --html-report tests/ # CI mode with reports
Configuration reference
| Key | Default | Description |
|---|---|---|
headless |
false |
Hide the browser window. |
browser |
"chromium" |
chromium (launch system Chrome) or electron (attach to a running Chrome/Electron over CDP). |
browser_args |
[] |
Extra browser launch flags. |
semantic_cache_enabled |
true |
In-session semantic cache (+200k score boost; feeds the scorer, never bypasses it). |
custom_controls_dirs |
["controls"] |
Directories scanned for @custom_control modules. |
timeout |
5000 |
Action timeout (ms). |
nav_timeout |
30000 |
Navigation timeout (ms). |
workers |
1 |
Max parallel hunt files. |
tests_home |
"tests" |
Default output for new hunts and scans. |
auto_annotate |
false |
Insert # 📍 Auto-Nav: comments on URL changes. |
channel |
null |
Chrome/Chromium binary to launch (chrome, msedge, chromium, …). |
executable_path |
null |
Explicit path to a Chrome/Chromium (or Electron) executable. |
retries |
0 |
Retry failed hunts N times. |
screenshot |
"on-fail" |
none, on-fail, or always. |
html_report |
false |
Generate reports/manul_report.html. |
explain_mode |
false |
Per-channel scoring breakdown in output. |
Environment variables (MANUL_HEADLESS, MANUL_BROWSER, MANUL_CHANNEL, MANUL_WORKERS, etc.) always override JSON config.
Docker
docker run --rm --shm-size=1g \
-v $(pwd)/hunts:/workspace/hunts:ro \
-v $(pwd)/reports:/workspace/reports \
ghcr.io/alexbeatnik/manul-engine:0.1.0 \
--html-report --screenshot on-fail hunts/
Non-root (manul, UID 1000), dumb-init as PID 1, --no-sandbox baked in. A docker-compose.yml ships with manul and manul-daemon services.
Embedding (Python API)
To drive a browser from a Python program — an assistant, an agent, a custom tool — use ManulSession, an async context manager that routes every call through the full heuristic pipeline. ManulEngine owns the browser lifecycle; the consumer never touches CDP directly:
from manul_engine import ManulSession
async with ManulSession(headless=True) as session:
await session.navigate("https://example.com/login")
await session.fill("Username field", "admin")
await session.click("Log in button")
await session.verify("Welcome")
The same deterministic scorer that runs .hunt files resolves each call by human label — no selectors. For agentic use, the agent commands (manul map / run-step / read / schema) expose the same capabilities as compact JSON over a running Chrome, with typed failure reasons and near candidates on a miss.
Ecosystem
| Component | Role | Links |
|---|---|---|
| ManulEngine | Deterministic automation runtime (Python). Heuristic element resolver, .hunt DSL, CLI runner. |
PyPI · GitHub |
| Manul Engine Extension | VS Code extension for ManulEngine with debug panel, explain mode, and Test Explorer integration. | Marketplace · Open VSX · GitHub |
| ManulMcpServer | MCP bridge that gives Copilot Chat and other agents access to ManulEngine. | Marketplace · Open VSX · GitHub |
| ManulAI Local Agent | Autonomous AI agent for browser automation, powered by ManulEngine. | Marketplace · Open VSX · GitHub |
Contributing and running tests
git clone https://github.com/alexbeatnik/ManulEngine.git
cd ManulEngine
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# Requires a system-installed Google Chrome / Chromium on PATH.
python run_tests.py # synthetic + unit suite
python demo/run_demo.py # integration hunts (needs network)
python demo/benchmarks/run_benchmarks.py # adversarial DOM fixtures
Get Involved
ManulEngine is alpha-stage and solo-developed. If deterministic, explainable browser automation interests you:
- Try it:
pip install manul-engine==0.1.0(needs system Chrome/Chromium) - File issues: github.com/alexbeatnik/ManulEngine/issues
What's New in v0.1.0
- Ollama / in-process LLM removed (BREAKING): ManulEngine is now purely deterministic — there is no in-engine model. The
model,ai_threshold,ai_always,ai_policysettings (andMANUL_MODEL/MANUL_AI_*/MANUL_LLM_*env vars) are gone, along with the free-text task planner and the What-If LLM analysis (the deterministic explain-next dry-run stays). Intelligence now lives in the external agent that drives the engine via the agent commands. - Playwright removed — native Chrome DevTools Protocol backend (BREAKING): the entire browser layer is now ManulEngine's own CDP client in
manul_engine/cdp/, driving system Chrome/Chromium over a raw WebSocket. The only runtime dependency iswebsockets(no Playwright, no Node.js, no bundled browser download).ManulSession.pageis now amanul_engine.cdp.CDPPage; per-frame iframe routing uses real per-frame execution contexts. Install requires a system Chrome/Chromium onPATH(playwright installis gone). browseris Chromium-only:firefox/webkitare no longer accepted (CDP is Chrome's protocol).browsernow selects launch mode —chromium(launch) orelectron(attach to a running Chrome/Electron over CDP); choose the binary withchannel/executable_path.- Agent CLI commands for external LLM drivers: new
manul schema/map/read/run-stepsubcommands emit compact JSON (stdout) while engine logs stay on stderr, attaching to an already-running Chrome over CDP — the surface an external model uses to see the page, act, and read by human label. Mirrors ManulEngine (Go)'s agent commands.
License
Version: 0.1.0
Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file manul_engine-0.1.0.tar.gz.
File metadata
- Download URL: manul_engine-0.1.0.tar.gz
- Upload date:
- Size: 207.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b82b61cfd65197973ded02eacf29e02fa963182204cff2a150fcc608410da198
|
|
| MD5 |
4b24625104e41f6bb42c49c093418f86
|
|
| BLAKE2b-256 |
aa29b5cf3741b6a33dd8cffdcca528870ea148a5bb05a2225d7e42306b3b51a8
|
Provenance
The following attestation bundles were made for manul_engine-0.1.0.tar.gz:
Publisher:
release.yml on alexbeatnik/ManulEngine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
manul_engine-0.1.0.tar.gz -
Subject digest:
b82b61cfd65197973ded02eacf29e02fa963182204cff2a150fcc608410da198 - Sigstore transparency entry: 2043923730
- Sigstore integration time:
-
Permalink:
alexbeatnik/ManulEngine@860b355d5c94e4512f15a1f2e3c74d688492486f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/alexbeatnik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@860b355d5c94e4512f15a1f2e3c74d688492486f -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file manul_engine-0.1.0-py3-none-any.whl.
File metadata
- Download URL: manul_engine-0.1.0-py3-none-any.whl
- Upload date:
- Size: 213.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed12da24ac892f194659e9eaaf3b8bf84448bfe3a21e725277682183dfbb4837
|
|
| MD5 |
180d4c15fa1f7a67ccd5257ce3b2d79a
|
|
| BLAKE2b-256 |
c5ffbd0f0be3557449efb1627b64b88dcbac1088d440ac28a0f2b9117c34a68e
|
Provenance
The following attestation bundles were made for manul_engine-0.1.0-py3-none-any.whl:
Publisher:
release.yml on alexbeatnik/ManulEngine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
manul_engine-0.1.0-py3-none-any.whl -
Subject digest:
ed12da24ac892f194659e9eaaf3b8bf84448bfe3a21e725277682183dfbb4837 - Sigstore transparency entry: 2043923749
- Sigstore integration time:
-
Permalink:
alexbeatnik/ManulEngine@860b355d5c94e4512f15a1f2e3c74d688492486f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/alexbeatnik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@860b355d5c94e4512f15a1f2e3c74d688492486f -
Trigger Event:
workflow_dispatch
-
Statement type: