@q-agent/agent 0.1.9 → 0.1.11
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/dist/src/runner.js +31 -3
- package/package.json +1 -1
- package/vendor/explore_session.cjs +16 -3
package/dist/src/runner.js
CHANGED
|
@@ -854,12 +854,40 @@ async function processExplorationJob(cfg, session) {
|
|
|
854
854
|
const args = [script, session.baseUrl];
|
|
855
855
|
if (storageState)
|
|
856
856
|
args.push(storageState);
|
|
857
|
-
|
|
857
|
+
// Run the explore browser HEADED on the paired device: the user watches the
|
|
858
|
+
// session, and a headed browser dodges WAF/bot-protection that blocks headless.
|
|
859
|
+
const child = (0, child_process_1.spawn)((0, paths_1.nodeBin)(), args, {
|
|
860
|
+
cwd: nm,
|
|
861
|
+
env: { ...nodePathEnv(nm), QAGENT_EXPLORE_HEADED: "1" },
|
|
862
|
+
});
|
|
858
863
|
activeChild = child;
|
|
864
|
+
// Capture the driver's stderr so a launch failure (missing browser, bad
|
|
865
|
+
// Playwright resolution, …) is visible instead of a silent "complete".
|
|
866
|
+
let driverStderr = "";
|
|
867
|
+
child.stderr?.on("data", (d) => {
|
|
868
|
+
driverStderr += String(d);
|
|
869
|
+
});
|
|
859
870
|
const driver = makeExploreDriver(child);
|
|
860
871
|
try {
|
|
861
|
-
// Wait for the driver's readiness signal before the first observe.
|
|
862
|
-
|
|
872
|
+
// Wait for the driver's readiness signal before the first observe. If the
|
|
873
|
+
// driver died before signalling ready, finalize with a clear driver-error
|
|
874
|
+
// (carrying its stderr) rather than letting the loop break silently.
|
|
875
|
+
const ready = await driver.ready();
|
|
876
|
+
if (!ready || ready.ok === false) {
|
|
877
|
+
const error = (ready && ready.error) || "driver failed to start";
|
|
878
|
+
const detail = driverStderr.trim();
|
|
879
|
+
await api
|
|
880
|
+
.postExploreFinalize(cfg, session.sessionId, {
|
|
881
|
+
discovered: { routes: [], selectors: [] },
|
|
882
|
+
log: [{ phase: "driver", error, stderr: detail || undefined }],
|
|
883
|
+
stopReason: "driver-error",
|
|
884
|
+
stepsTaken: 0,
|
|
885
|
+
})
|
|
886
|
+
.catch((err) => console.error("postExploreFinalize failed:", err));
|
|
887
|
+
(0, bus_1.emit)("explore-complete", { sessionId: session.sessionId, stopReason: "driver-error", error });
|
|
888
|
+
console.error(`Exploration ${session.sessionId} driver-error: ${error}${detail ? ` — ${detail}` : ""}`);
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
863
891
|
await runExplorationLoop(cfg, session, driver, api, bus_1.emit);
|
|
864
892
|
}
|
|
865
893
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@q-agent/agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Q-Agent Local Agent — claims execution jobs from the Q-Agent server and runs Playwright locally, so manual login/MFA happens on the user's own machine and session credentials never leave it.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
//
|
|
19
19
|
// Protocol (one JSON object per line, each way):
|
|
20
20
|
// {"cmd":"observe"}
|
|
21
|
-
// -> {"ok":true,"url":..,"path":..,"a11y":<
|
|
21
|
+
// -> {"ok":true,"url":..,"path":..,"a11y":<ariaSnapshot() role+name tree>,
|
|
22
22
|
// "elements":[<distilled interactive DOM>]}
|
|
23
23
|
// {"cmd":"act","action":"goto|click|fill|expectVisible","args":{...}}
|
|
24
24
|
// -> {"ok":bool,"error":str|null,"changed":bool}
|
|
@@ -121,7 +121,11 @@ async function doAct(action, args) {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
async function doObserve() {
|
|
124
|
-
|
|
124
|
+
// Playwright removed page.accessibility; the supported role+name tree is
|
|
125
|
+
// locator.ariaSnapshot() (a compact YAML string, ideal for the model and
|
|
126
|
+
// maps directly to getByRole). Best-effort — never fail observe on it.
|
|
127
|
+
let a11y = '';
|
|
128
|
+
try { a11y = await page.locator('body').ariaSnapshot(); } catch { a11y = ''; }
|
|
125
129
|
const elements = await page.evaluate(DISTILL);
|
|
126
130
|
const url = page.url();
|
|
127
131
|
let path = '';
|
|
@@ -155,12 +159,21 @@ async function handle(line) {
|
|
|
155
159
|
|
|
156
160
|
(async () => {
|
|
157
161
|
if (!baseURL) { console.error('explore_session: missing baseURL arg'); process.exit(1); }
|
|
158
|
-
|
|
162
|
+
// Headed when QAGENT_EXPLORE_HEADED=1 (the Local Agent sets it so the user can
|
|
163
|
+
// watch, and a headed browser trips WAF/bot-protection far less than headless);
|
|
164
|
+
// headless otherwise (e.g. the server, which has no display).
|
|
165
|
+
browser = await chromium.launch({ headless: process.env.QAGENT_EXPLORE_HEADED !== '1' });
|
|
159
166
|
const contextOpts = { baseURL };
|
|
160
167
|
if (storageState) contextOpts.storageState = storageState;
|
|
161
168
|
const context = await browser.newContext(contextOpts);
|
|
162
169
|
page = await context.newPage();
|
|
163
170
|
|
|
171
|
+
// Land on the app before the first observe so step 1 sees the real page,
|
|
172
|
+
// not about:blank. Best-effort — the model can still goto elsewhere.
|
|
173
|
+
if (baseURL) {
|
|
174
|
+
try { await page.goto(baseURL, { waitUntil: 'domcontentloaded' }); } catch {}
|
|
175
|
+
}
|
|
176
|
+
|
|
164
177
|
// Serialize commands: one line in -> one response out, in order.
|
|
165
178
|
const rl = readline.createInterface({ input: process.stdin });
|
|
166
179
|
let chain = Promise.resolve();
|