@mjasnikovs/pi-task 0.40.34 → 0.40.36
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.
|
@@ -113,9 +113,9 @@ export interface DeepSessionFacts {
|
|
|
113
113
|
* The three request-shaped facts, computed from the log and from nothing else. The
|
|
114
114
|
* driver records `sessionRequests` and calls this; the values are exactly what the
|
|
115
115
|
* pre-log driver computed by filtering the same map (the sign-in request is the
|
|
116
|
-
* first same-origin non-GET issued at or after the submit; the data requests are
|
|
117
|
-
* same-origin
|
|
118
|
-
*
|
|
116
|
+
* first same-origin non-GET issued at or after the submit; the data requests are
|
|
117
|
+
* the same-origin XHR/fetch issued at or after the sign-in request, that request
|
|
118
|
+
* itself excluded).
|
|
119
119
|
*/
|
|
120
120
|
export declare function deriveLegacyFacts(log: SessionRequest[]): Pick<DeepSessionFacts, 'authRequest' | 'postAuthDataAttempted' | 'postAuthData2xx'>;
|
|
121
121
|
/**
|
|
@@ -202,9 +202,9 @@ export function pinnedLocalPort(vars) {
|
|
|
202
202
|
* The three request-shaped facts, computed from the log and from nothing else. The
|
|
203
203
|
* driver records `sessionRequests` and calls this; the values are exactly what the
|
|
204
204
|
* pre-log driver computed by filtering the same map (the sign-in request is the
|
|
205
|
-
* first same-origin non-GET issued at or after the submit; the data requests are
|
|
206
|
-
* same-origin
|
|
207
|
-
*
|
|
205
|
+
* first same-origin non-GET issued at or after the submit; the data requests are
|
|
206
|
+
* the same-origin XHR/fetch issued at or after the sign-in request, that request
|
|
207
|
+
* itself excluded).
|
|
208
208
|
*/
|
|
209
209
|
export function deriveLegacyFacts(log) {
|
|
210
210
|
const auth = log.find(r => r.phase === 'auth') ?? null;
|
|
@@ -291,8 +291,12 @@ export function judgeDeepSession(f) {
|
|
|
291
291
|
// `GET /*` → index.html fallback returns 200 for a route it does not have, so
|
|
292
292
|
// the status is healthy and the body is the app shell. A document navigation
|
|
293
293
|
// answered with HTML is normal; an XHR asking for data and getting HTML is a
|
|
294
|
-
// call that reached nothing.
|
|
295
|
-
|
|
294
|
+
// call that reached nothing. The sign-in request is exempt: it keeps the first
|
|
295
|
+
// hop but carries the LAST hop's mime, so a fetch login that 302s to a page
|
|
296
|
+
// reads as HTML here while being a sign-in that worked.
|
|
297
|
+
const swallowed = (f.sessionRequests ?? []).find(r => r.initiator === 'xhr'
|
|
298
|
+
&& r.phase !== 'auth'
|
|
299
|
+
&& (r.mimeType ?? '').startsWith('text/html'));
|
|
296
300
|
if (swallowed) {
|
|
297
301
|
return {
|
|
298
302
|
outcome: 'fail',
|
|
@@ -655,10 +659,15 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
655
659
|
// judges it is the chain's last, so the first hop stays and the eventual
|
|
656
660
|
// response lands on it. Overwriting reads a POST that 302s as a GET, and
|
|
657
661
|
// then no sign-in request is ever found.
|
|
658
|
-
|
|
659
|
-
|
|
662
|
+
const req = p.request;
|
|
663
|
+
const existing = requests.get(id);
|
|
664
|
+
if (p.redirectResponse !== undefined && existing) {
|
|
665
|
+
existing.finalUrl = String(req?.url ?? existing.finalUrl);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
660
668
|
requests.set(id, {
|
|
661
669
|
url: String(req?.url ?? ''),
|
|
670
|
+
finalUrl: String(req?.url ?? ''),
|
|
662
671
|
method: String(req?.method ?? 'GET'),
|
|
663
672
|
type: String(p.type ?? ''),
|
|
664
673
|
status: null,
|
|
@@ -702,15 +711,15 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
702
711
|
const before = await evaluate(INSPECT_EXPR);
|
|
703
712
|
if (!before)
|
|
704
713
|
throw new Error('the page could not be inspected');
|
|
705
|
-
const sameOrigin = (r) => r.
|
|
714
|
+
const sameOrigin = (r) => r.finalUrl.startsWith(`${origin}/`) || r.finalUrl === origin;
|
|
706
715
|
const isData = (r) => r.type === 'XHR' || r.type === 'Fetch';
|
|
707
716
|
const foreignOriginFailures = () => {
|
|
708
717
|
const out = new Set();
|
|
709
718
|
for (const r of requests.values()) {
|
|
710
|
-
if (sameOrigin(r) || !r.failed || !r.
|
|
719
|
+
if (sameOrigin(r) || !r.failed || !r.finalUrl.startsWith('http'))
|
|
711
720
|
continue;
|
|
712
721
|
try {
|
|
713
|
-
out.add(new URL(r.
|
|
722
|
+
out.add(new URL(r.finalUrl).origin);
|
|
714
723
|
}
|
|
715
724
|
catch {
|
|
716
725
|
// unparseable url — nothing to name
|
|
@@ -721,10 +730,11 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
721
730
|
const initiatorOf = (r) => isData(r) ? 'xhr'
|
|
722
731
|
: r.type === 'Document' ? 'document'
|
|
723
732
|
: 'other';
|
|
724
|
-
/** The same-origin request log
|
|
725
|
-
*
|
|
726
|
-
*
|
|
727
|
-
*
|
|
733
|
+
/** The same-origin request log. `postSeq` is where the authenticated half
|
|
734
|
+
* begins — Infinity while no submit has happened — so everything before it is
|
|
735
|
+
* 'pre'; the sign-in request itself is 'auth', not 'post'. The boundary is the
|
|
736
|
+
* sign-in request, not the submit: a CSRF token or a beacon the submit fires
|
|
737
|
+
* BEFORE the login is not evidence about the authenticated client. */
|
|
728
738
|
const sessionLog = (authId, postSeq) => [...requests]
|
|
729
739
|
.filter(([, r]) => sameOrigin(r))
|
|
730
740
|
.map(([id, r]) => ({
|
|
@@ -755,6 +765,7 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
755
765
|
const unsubmitted = (over) => facts(sessionLog(null, Number.POSITIVE_INFINITY), over);
|
|
756
766
|
if (!before.hasPassword || credentials === null)
|
|
757
767
|
return judge(unsubmitted({}));
|
|
768
|
+
const fillSeq = nextSeq;
|
|
758
769
|
const filled = await evaluate(fillExpr(credentials.identifier, credentials.password));
|
|
759
770
|
if (!filled?.ok)
|
|
760
771
|
return judge(unsubmitted({ submitted: false }));
|
|
@@ -780,6 +791,18 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
780
791
|
break;
|
|
781
792
|
}
|
|
782
793
|
}
|
|
794
|
+
// Nothing after the submit: a form that submits from the fill's own input
|
|
795
|
+
// events already sent it. Only a NAVIGATION qualifies here — a form submission
|
|
796
|
+
// is one, and the background traffic the fill races (beacons, telemetry) is
|
|
797
|
+
// XHR or fetch, never a document.
|
|
798
|
+
if (authId === null) {
|
|
799
|
+
for (const [id, r] of requests) {
|
|
800
|
+
if (r.seq >= fillSeq && sameOrigin(r) && r.method !== 'GET' && r.type === 'Document') {
|
|
801
|
+
authId = id;
|
|
802
|
+
break;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
783
806
|
const authReq = authId === null ? null : requests.get(authId);
|
|
784
807
|
const now = await evaluate(INSPECT_EXPR);
|
|
785
808
|
const domJudgment = judgeRenderedDom(now?.html ?? '');
|
|
@@ -801,7 +824,7 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
801
824
|
lastActivity = Date.now();
|
|
802
825
|
await settle(() => lastActivity, RE_NAV_CAP_MS, quietMs);
|
|
803
826
|
}
|
|
804
|
-
return judge(facts(sessionLog(authId, submitSeq), {
|
|
827
|
+
return judge(facts(sessionLog(authId, authReq?.seq ?? submitSeq), {
|
|
805
828
|
submitted: true,
|
|
806
829
|
foreignOriginFailures: foreignOriginFailures(),
|
|
807
830
|
leftAuthWall,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.36",
|
|
4
4
|
"description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|