forgeqe-cli 2.2.1__tar.gz → 2.2.3__tar.gz
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.
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/PKG-INFO +3 -3
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/README.md +2 -2
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/py/forgeqe_cli/__init__.py +1 -1
- forgeqe_cli-2.2.3/py/forgeqe_cli/pytest_plugin.py +132 -0
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/py/forgeqe_cli.egg-info/PKG-INFO +3 -3
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/py/forgeqe_cli.egg-info/SOURCES.txt +1 -0
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/py/forgeqe_cli.egg-info/entry_points.txt +3 -0
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/pyproject.toml +5 -1
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/LICENSE +0 -0
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/py/forgeqe_cli/__main__.py +0 -0
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/py/forgeqe_cli/launcher.py +0 -0
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/py/forgeqe_cli.egg-info/dependency_links.txt +0 -0
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/py/forgeqe_cli.egg-info/requires.txt +0 -0
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/py/forgeqe_cli.egg-info/top_level.txt +0 -0
- {forgeqe_cli-2.2.1 → forgeqe_cli-2.2.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: forgeqe-cli
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.3
|
|
4
4
|
Summary: pip launcher for the ForgeQE browser agent (npm package: @capagents/forgeqe)
|
|
5
5
|
Author: CLI-Agents
|
|
6
6
|
License: MIT
|
|
@@ -65,7 +65,7 @@ ForgeQE sends no telemetry and turns it off in everything it starts:
|
|
|
65
65
|
- The agent runtime: auto-update, session sharing, OpenTelemetry spans, and the model catalogue download are off; it uses its bundled model list.
|
|
66
66
|
- The repo's test command: `DO_NOT_TRACK=1`, plus the opt-outs for Next.js, Nuxt, Astro, Gatsby, Storybook, Turborepo, Angular, .NET, and Cypress.
|
|
67
67
|
|
|
68
|
-
`OTEL_EXPORTER_OTLP_*` variables are removed from those processes, and your environment cannot turn any of this back on. The
|
|
68
|
+
`OTEL_EXPORTER_OTLP_*` variables are removed from those processes, and your environment cannot turn any of this back on. The traffic is to your model endpoint and the sites the agent visits. Besides that, ForgeQE downloads only public files: the model price lists from models.dev and LiteLLM, once a day (`FORGEQE_PRICES=off` stops them); the agent runtime from its GitHub releases, when it is not installed; and the engine's ad-block and cookie-banner extensions from the Chrome Web Store, once. Nothing about your runs is sent.
|
|
69
69
|
|
|
70
70
|
## Configure
|
|
71
71
|
|
|
@@ -102,7 +102,7 @@ The active profile is `llm.active` in `forgeqe.yaml`, else `default` in `llms.js
|
|
|
102
102
|
|
|
103
103
|
**Model router.** With more than one model in `llms.json`, `llm.routing: auto` gives each area of work (ForgeQE's agent, browser runs, coding, review, analysis, planning) the cheapest configured model that is good enough for it. ForgeQE checks each model once and rates it, and keeps the result in `.forgeqe/model-profile.json`. `forgeqe models --routes` shows the model each area gets, and why. `--profile` and `code.model` still win. See [docs/MODEL_ROUTING.md](docs/MODEL_ROUTING.md).
|
|
104
104
|
|
|
105
|
-
**GitHub Copilot.** With Copilot licences, a `github_copilot` profile runs
|
|
105
|
+
**GitHub Copilot.** With Copilot licences, a `github_copilot` profile runs the browser engine and the desktop and mobile agents on the Copilot seat through GitHub's Copilot SDK, behind a gateway on `127.0.0.1`. It signs in with a GitHub token (`forgeqe auth login copilot`: a fine-grained token with the Copilot Requests permission, or the GitHub CLI's sign-in), so no third-party app is authorized on the account. ForgeQE's agent and Scribe do not run on Copilot yet. See HOW_TO_USE.md, *GitHub Copilot seats as the model*.
|
|
106
106
|
|
|
107
107
|
## CLI
|
|
108
108
|
|
|
@@ -51,7 +51,7 @@ ForgeQE sends no telemetry and turns it off in everything it starts:
|
|
|
51
51
|
- The agent runtime: auto-update, session sharing, OpenTelemetry spans, and the model catalogue download are off; it uses its bundled model list.
|
|
52
52
|
- The repo's test command: `DO_NOT_TRACK=1`, plus the opt-outs for Next.js, Nuxt, Astro, Gatsby, Storybook, Turborepo, Angular, .NET, and Cypress.
|
|
53
53
|
|
|
54
|
-
`OTEL_EXPORTER_OTLP_*` variables are removed from those processes, and your environment cannot turn any of this back on. The
|
|
54
|
+
`OTEL_EXPORTER_OTLP_*` variables are removed from those processes, and your environment cannot turn any of this back on. The traffic is to your model endpoint and the sites the agent visits. Besides that, ForgeQE downloads only public files: the model price lists from models.dev and LiteLLM, once a day (`FORGEQE_PRICES=off` stops them); the agent runtime from its GitHub releases, when it is not installed; and the engine's ad-block and cookie-banner extensions from the Chrome Web Store, once. Nothing about your runs is sent.
|
|
55
55
|
|
|
56
56
|
## Configure
|
|
57
57
|
|
|
@@ -88,7 +88,7 @@ The active profile is `llm.active` in `forgeqe.yaml`, else `default` in `llms.js
|
|
|
88
88
|
|
|
89
89
|
**Model router.** With more than one model in `llms.json`, `llm.routing: auto` gives each area of work (ForgeQE's agent, browser runs, coding, review, analysis, planning) the cheapest configured model that is good enough for it. ForgeQE checks each model once and rates it, and keeps the result in `.forgeqe/model-profile.json`. `forgeqe models --routes` shows the model each area gets, and why. `--profile` and `code.model` still win. See [docs/MODEL_ROUTING.md](docs/MODEL_ROUTING.md).
|
|
90
90
|
|
|
91
|
-
**GitHub Copilot.** With Copilot licences, a `github_copilot` profile runs
|
|
91
|
+
**GitHub Copilot.** With Copilot licences, a `github_copilot` profile runs the browser engine and the desktop and mobile agents on the Copilot seat through GitHub's Copilot SDK, behind a gateway on `127.0.0.1`. It signs in with a GitHub token (`forgeqe auth login copilot`: a fine-grained token with the Copilot Requests permission, or the GitHub CLI's sign-in), so no third-party app is authorized on the account. ForgeQE's agent and Scribe do not run on Copilot yet. See HOW_TO_USE.md, *GitHub Copilot seats as the model*.
|
|
92
92
|
|
|
93
93
|
## CLI
|
|
94
94
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""ForgeQE plugin for pytest: an HTML report after every run, from the terminal, an IDE's test runner, or CI.
|
|
2
|
+
|
|
3
|
+
pytest loads it by itself from the forgeqe-cli wheel (the pytest11 entry point). It is on in a folder with
|
|
4
|
+
forgeqe.yaml or .forgeqe/, or with ``pytest --forgeqe-report`` or FORGEQE_REPORT=1; FORGEQE_REPORT=0 turns it off.
|
|
5
|
+
At the end of the run it writes the results, with each failure's message and traceback, to
|
|
6
|
+
.pytest_cache/forgeqe/results.json and runs ``forgeqe report`` on it.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
import time
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
ANSI = re.compile(r"\x1b\[[0-9;]*[A-Za-z]")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _clean(text: str) -> str:
|
|
25
|
+
return ANSI.sub("", text or "")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def pytest_addoption(parser):
|
|
29
|
+
parser.getgroup("forgeqe").addoption(
|
|
30
|
+
"--forgeqe-report", action="store_true", default=False, help="write a ForgeQE HTML report after the run"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _active(config) -> bool:
|
|
35
|
+
flag = os.environ.get("FORGEQE_REPORT")
|
|
36
|
+
if flag == "0":
|
|
37
|
+
return False
|
|
38
|
+
if flag == "1" or config.getoption("forgeqe_report", False):
|
|
39
|
+
return True
|
|
40
|
+
root = Path(str(config.rootpath))
|
|
41
|
+
return (root / "forgeqe.yaml").exists() or (root / ".forgeqe").is_dir()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def pytest_configure(config):
|
|
45
|
+
# An xdist worker sends its results to the controller, which writes the one report.
|
|
46
|
+
if hasattr(config, "workerinput") or not _active(config):
|
|
47
|
+
return
|
|
48
|
+
config.pluginmanager.register(_Collector(config), "forgeqe-collector")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _message(report) -> str:
|
|
52
|
+
crash = getattr(getattr(report, "longrepr", None), "reprcrash", None)
|
|
53
|
+
text = crash.message if crash is not None else str(getattr(report, "longrepr", "") or "")
|
|
54
|
+
first = next((line.strip() for line in _clean(text).splitlines() if line.strip()), "")
|
|
55
|
+
return first[:300]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class _Collector:
|
|
59
|
+
def __init__(self, config):
|
|
60
|
+
self.config = config
|
|
61
|
+
self.tests: dict[str, dict] = {}
|
|
62
|
+
self.started = datetime.now(timezone.utc)
|
|
63
|
+
self.t0 = time.monotonic()
|
|
64
|
+
self.line = ""
|
|
65
|
+
|
|
66
|
+
def pytest_runtest_logreport(self, report):
|
|
67
|
+
nodeid = report.nodeid
|
|
68
|
+
test = self.tests.setdefault(
|
|
69
|
+
nodeid, {"name": " › ".join(nodeid.split("::")), "status": "passed", "seconds": 0.0, "file": report.location[0]}
|
|
70
|
+
)
|
|
71
|
+
if report.outcome == "rerun":
|
|
72
|
+
# pytest-rerunfailures: a failure that is run again; passing later makes the test flaky, not failed.
|
|
73
|
+
test["rerun"] = True
|
|
74
|
+
test.setdefault("message", _message(report))
|
|
75
|
+
test.setdefault("details", _clean(report.longreprtext))
|
|
76
|
+
return
|
|
77
|
+
test["seconds"] = round(test["seconds"] + (report.duration or 0), 3)
|
|
78
|
+
if report.failed:
|
|
79
|
+
if test["status"] != "failed":
|
|
80
|
+
test["message"] = _message(report)
|
|
81
|
+
test["details"] = _clean(report.longreprtext)
|
|
82
|
+
test["status"] = "failed"
|
|
83
|
+
elif report.skipped and test["status"] == "passed" and report.when in ("setup", "call"):
|
|
84
|
+
test["status"] = "skipped"
|
|
85
|
+
|
|
86
|
+
def pytest_sessionfinish(self, session):
|
|
87
|
+
if not self.tests:
|
|
88
|
+
return
|
|
89
|
+
tests = []
|
|
90
|
+
for test in self.tests.values():
|
|
91
|
+
rerun = test.pop("rerun", False)
|
|
92
|
+
if test["status"] == "passed" and rerun:
|
|
93
|
+
test["flaky"] = True
|
|
94
|
+
elif test["status"] != "failed" and not test.get("flaky"):
|
|
95
|
+
test.pop("message", None)
|
|
96
|
+
test.pop("details", None)
|
|
97
|
+
tests.append({k: v for k, v in test.items() if v not in ("", None)})
|
|
98
|
+
doc = {
|
|
99
|
+
"format": "forgeqe-results",
|
|
100
|
+
"version": 1,
|
|
101
|
+
"framework": "pytest",
|
|
102
|
+
"startedAt": self.started.isoformat(),
|
|
103
|
+
"ms": round((time.monotonic() - self.t0) * 1000),
|
|
104
|
+
"command": " ".join(["pytest", *self.config.invocation_params.args])[:500],
|
|
105
|
+
"tests": tests,
|
|
106
|
+
}
|
|
107
|
+
cache = getattr(self.config, "cache", None)
|
|
108
|
+
folder = Path(cache.mkdir("forgeqe")) if cache is not None else Path(tempfile.mkdtemp(prefix="forgeqe-"))
|
|
109
|
+
path = folder / "results.json"
|
|
110
|
+
path.write_text(json.dumps(doc, indent=2) + "\n", encoding="utf-8")
|
|
111
|
+
self.line = _report(path, Path(str(self.config.rootpath)))
|
|
112
|
+
|
|
113
|
+
def pytest_terminal_summary(self, terminalreporter):
|
|
114
|
+
if self.line:
|
|
115
|
+
terminalreporter.write_line(self.line)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _report(path: Path, root: Path) -> str:
|
|
119
|
+
"""Run forgeqe report on the results; the line to show."""
|
|
120
|
+
# This wheel's own launcher, by the Python running pytest: no PATH lookup and no .cmd shim on Windows.
|
|
121
|
+
bin_ = os.environ.get("FORGEQE_BIN")
|
|
122
|
+
cmd = [bin_, "report", str(path)] if bin_ else [sys.executable, "-m", "forgeqe_cli", "report", str(path)]
|
|
123
|
+
try:
|
|
124
|
+
run = subprocess.run(cmd, cwd=str(root), capture_output=True, text=True, timeout=300)
|
|
125
|
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
126
|
+
return f"ForgeQE: no report ({exc.__class__.__name__}). Build it with: forgeqe report {path}"
|
|
127
|
+
# forgeqe report exits 1 when a test failed: that is a report, not an error.
|
|
128
|
+
line = next((l for l in _clean(run.stdout).splitlines() if l.startswith("Report")), "") if run.returncode <= 1 else ""
|
|
129
|
+
if line:
|
|
130
|
+
return "ForgeQE report: " + line[len("Report"):].strip()
|
|
131
|
+
why = (_clean(run.stderr).strip().splitlines() or ["it did not finish"])[-1]
|
|
132
|
+
return f"ForgeQE: no report ({why}). Build it with: forgeqe report {path}"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: forgeqe-cli
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.3
|
|
4
4
|
Summary: pip launcher for the ForgeQE browser agent (npm package: @capagents/forgeqe)
|
|
5
5
|
Author: CLI-Agents
|
|
6
6
|
License: MIT
|
|
@@ -65,7 +65,7 @@ ForgeQE sends no telemetry and turns it off in everything it starts:
|
|
|
65
65
|
- The agent runtime: auto-update, session sharing, OpenTelemetry spans, and the model catalogue download are off; it uses its bundled model list.
|
|
66
66
|
- The repo's test command: `DO_NOT_TRACK=1`, plus the opt-outs for Next.js, Nuxt, Astro, Gatsby, Storybook, Turborepo, Angular, .NET, and Cypress.
|
|
67
67
|
|
|
68
|
-
`OTEL_EXPORTER_OTLP_*` variables are removed from those processes, and your environment cannot turn any of this back on. The
|
|
68
|
+
`OTEL_EXPORTER_OTLP_*` variables are removed from those processes, and your environment cannot turn any of this back on. The traffic is to your model endpoint and the sites the agent visits. Besides that, ForgeQE downloads only public files: the model price lists from models.dev and LiteLLM, once a day (`FORGEQE_PRICES=off` stops them); the agent runtime from its GitHub releases, when it is not installed; and the engine's ad-block and cookie-banner extensions from the Chrome Web Store, once. Nothing about your runs is sent.
|
|
69
69
|
|
|
70
70
|
## Configure
|
|
71
71
|
|
|
@@ -102,7 +102,7 @@ The active profile is `llm.active` in `forgeqe.yaml`, else `default` in `llms.js
|
|
|
102
102
|
|
|
103
103
|
**Model router.** With more than one model in `llms.json`, `llm.routing: auto` gives each area of work (ForgeQE's agent, browser runs, coding, review, analysis, planning) the cheapest configured model that is good enough for it. ForgeQE checks each model once and rates it, and keeps the result in `.forgeqe/model-profile.json`. `forgeqe models --routes` shows the model each area gets, and why. `--profile` and `code.model` still win. See [docs/MODEL_ROUTING.md](docs/MODEL_ROUTING.md).
|
|
104
104
|
|
|
105
|
-
**GitHub Copilot.** With Copilot licences, a `github_copilot` profile runs
|
|
105
|
+
**GitHub Copilot.** With Copilot licences, a `github_copilot` profile runs the browser engine and the desktop and mobile agents on the Copilot seat through GitHub's Copilot SDK, behind a gateway on `127.0.0.1`. It signs in with a GitHub token (`forgeqe auth login copilot`: a fine-grained token with the Copilot Requests permission, or the GitHub CLI's sign-in), so no third-party app is authorized on the account. ForgeQE's agent and Scribe do not run on Copilot yet. See HOW_TO_USE.md, *GitHub Copilot seats as the model*.
|
|
106
106
|
|
|
107
107
|
## CLI
|
|
108
108
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "forgeqe-cli"
|
|
7
|
-
version = "2.2.
|
|
7
|
+
version = "2.2.3"
|
|
8
8
|
description = "pip launcher for the ForgeQE browser agent (npm package: @capagents/forgeqe)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -21,6 +21,10 @@ forgeqe = "forgeqe_cli.launcher:main"
|
|
|
21
21
|
# The command's name before the rename, kept so existing scripts still work.
|
|
22
22
|
webpilot = "forgeqe_cli.launcher:main"
|
|
23
23
|
|
|
24
|
+
# pytest loads the plugin by itself where the wheel is installed: a ForgeQE report after every run (forgeqe report setup).
|
|
25
|
+
[project.entry-points.pytest11]
|
|
26
|
+
forgeqe = "forgeqe_cli.pytest_plugin"
|
|
27
|
+
|
|
24
28
|
[tool.setuptools.packages.find]
|
|
25
29
|
where = ["py"]
|
|
26
30
|
# The engine ships in the npm package; the wheel is only the launcher.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|