@ia-qa/self-healing 1.24.0 → 1.26.0
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/README.md +24 -1
- package/TUTORIAL.md +23 -3
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/fix.js +23 -0
- package/dist/cli/fix.js.map +1 -1
- package/dist/cli/login.d.ts +101 -4
- package/dist/cli/login.js +247 -46
- package/dist/cli/login.js.map +1 -1
- package/dist/cli/map.d.ts +21 -0
- package/dist/cli/map.js +46 -1
- package/dist/cli/map.js.map +1 -1
- package/dist/cli/session.d.ts +27 -0
- package/dist/cli/session.js +48 -29
- package/dist/cli/session.js.map +1 -1
- package/dist/explain.d.ts +14 -0
- package/dist/explain.js +23 -7
- package/dist/explain.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/ingest.d.ts +15 -1
- package/dist/ingest.js +50 -12
- package/dist/ingest.js.map +1 -1
- package/dist/sessionCheck.d.ts +25 -0
- package/dist/sessionCheck.js +46 -0
- package/dist/sessionCheck.js.map +1 -1
- package/dist/unrepaired.d.ts +31 -0
- package/dist/unrepaired.js +0 -0
- package/dist/unrepaired.js.map +1 -1
- package/package.json +1 -1
- package/skills/ia-qa-heal/SKILL.md +6 -2
package/dist/cli/login.js
CHANGED
|
@@ -38,40 +38,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.runLogin = runLogin;
|
|
40
40
|
exports.replaySession = replaySession;
|
|
41
|
+
exports.loginProven = loginProven;
|
|
42
|
+
exports.watchForLogin = watchForLogin;
|
|
41
43
|
exports.sessionDecision = sessionDecision;
|
|
42
44
|
const fs = __importStar(require("fs"));
|
|
43
45
|
const path = __importStar(require("path"));
|
|
44
46
|
const prompts_1 = __importDefault(require("prompts"));
|
|
45
47
|
const launcher_1 = require("../launcher");
|
|
46
48
|
const map_1 = require("./map");
|
|
49
|
+
const sessionCheck_1 = require("../sessionCheck");
|
|
47
50
|
const config_1 = require("../config");
|
|
48
|
-
|
|
49
|
-
* `ia-qa-heal login` — open a browser, let a human log in, keep the session.
|
|
50
|
-
*
|
|
51
|
-
* `config.auth` fills a form: a username field, a password field, a submit button. That
|
|
52
|
-
* describes *one shape of login*, and every app whose login is not that shape is out of
|
|
53
|
-
* reach — federated SSO, MFA, a consent screen, a magic link, a device check, a captcha.
|
|
54
|
-
* No selector list gets through any of them, so "add an auth block" is the wrong answer
|
|
55
|
-
* to most walls, and `map` was left mapping a login form under the name `dashboard`.
|
|
56
|
-
*
|
|
57
|
-
* This verb models nothing at all, which is why it is the general one: the human performs
|
|
58
|
-
* whatever the app asks, and the resulting session is saved in Playwright's `storageState`
|
|
59
|
-
* shape and reused by `map`. It cannot know *how* your app authenticates, and never needs to.
|
|
60
|
-
*
|
|
61
|
-
* Deliberately not automatic, and never part of a gate:
|
|
62
|
-
* - It is the only caller that opens a **visible** browser (`headed`) — everything that
|
|
63
|
-
* produces a contract stays headless, because a window in CI is a hung build.
|
|
64
|
-
* - It refuses without a TTY and under CI: it waits for a person twice over (at the
|
|
65
|
-
* browser, then at the keyboard), and a pipeline that runs it just hangs.
|
|
66
|
-
* - What it writes is a **secret** — live cookies, i.e. whoever holds the file is logged
|
|
67
|
-
* in as that user. So it announces the path before opening anything, and writes
|
|
68
|
-
* `.ia-qa/.gitignore` so a commit cannot carry it. The file never leaves the machine:
|
|
69
|
-
* nothing in this package uploads it.
|
|
70
|
-
*
|
|
71
|
-
* For CI, `config.auth` (reproducible, no human) and capture-during-run (`IAQA_CAPTURE=1`
|
|
72
|
-
* with your own suite's login) remain the two paths. A saved session is a local artefact
|
|
73
|
-
* with a shelf life — when it expires, `map` hits the wall again and says to re-run this.
|
|
74
|
-
*/
|
|
51
|
+
const sessionCheck_2 = require("../sessionCheck");
|
|
75
52
|
async function runLogin(args) {
|
|
76
53
|
if (process.env.CI) {
|
|
77
54
|
console.error(`❌ Refusing to open a login browser under CI.\n\n` +
|
|
@@ -80,14 +57,14 @@ async function runLogin(args) {
|
|
|
80
57
|
` "auth" in .ia-qa/config.json fills a login form from env/SSM secrets\n` +
|
|
81
58
|
` IAQA_CAPTURE=1 + testCommand your own suite logs in; map rides along`);
|
|
82
59
|
process.exitCode = 2;
|
|
83
|
-
return;
|
|
60
|
+
return { ok: false };
|
|
84
61
|
}
|
|
85
62
|
if (!process.stdin.isTTY) {
|
|
86
63
|
console.error(`❌ \`ia-qa-heal login\` needs a terminal: it opens a browser, then waits for you to say\n` +
|
|
87
64
|
` you are logged in. With stdin closed it would wait forever.\n` +
|
|
88
65
|
` Run it in your own shell — an agent cannot do this step for you.`);
|
|
89
66
|
process.exitCode = 2;
|
|
90
|
-
return;
|
|
67
|
+
return { ok: false };
|
|
91
68
|
}
|
|
92
69
|
const config = (0, config_1.loadConfig)();
|
|
93
70
|
const urlIdx = args.indexOf('--url');
|
|
@@ -95,7 +72,7 @@ async function runLogin(args) {
|
|
|
95
72
|
if (urlIdx !== -1 && (!wanted || wanted.startsWith('-'))) {
|
|
96
73
|
console.error('❌ --url needs a URL or a path (e.g. --url /dashboard).');
|
|
97
74
|
process.exitCode = 2;
|
|
98
|
-
return;
|
|
75
|
+
return { ok: false };
|
|
99
76
|
}
|
|
100
77
|
const target = new URL(wanted || config.pages[0]?.url || '/', config.baseUrl).toString();
|
|
101
78
|
// Written where this project keeps its session, not always the default: someone who
|
|
@@ -106,9 +83,19 @@ async function runLogin(args) {
|
|
|
106
83
|
if (sessionIdx !== -1 && (!explicit || explicit.startsWith('-'))) {
|
|
107
84
|
console.error('❌ --session needs a file path.');
|
|
108
85
|
process.exitCode = 2;
|
|
109
|
-
return;
|
|
86
|
+
return { ok: false };
|
|
110
87
|
}
|
|
111
88
|
const file = (0, config_1.sessionTarget)(config, explicit).file;
|
|
89
|
+
// How long to watch the window before falling back to "tell me what is in there".
|
|
90
|
+
// `--wait 0` turns the watch off and restores the plain press-Enter flow, which is the
|
|
91
|
+
// escape hatch for an app whose login cannot be observed from the outside at all.
|
|
92
|
+
const waitIdx = args.indexOf('--wait');
|
|
93
|
+
const waitSeconds = waitIdx !== -1 ? Number(args[waitIdx + 1]) : 600;
|
|
94
|
+
if (waitIdx !== -1 && (!Number.isFinite(waitSeconds) || waitSeconds < 0)) {
|
|
95
|
+
console.error('❌ --wait needs a number of seconds (0 turns the watch off).');
|
|
96
|
+
process.exitCode = 2;
|
|
97
|
+
return { ok: false };
|
|
98
|
+
}
|
|
112
99
|
console.log(`\n🔑 ia-qa-heal login`);
|
|
113
100
|
console.log(` Opens a visible browser at ${target}.`);
|
|
114
101
|
console.log(` Log in there however your app asks — SSO, MFA, a consent screen, all of it.`);
|
|
@@ -122,7 +109,7 @@ async function runLogin(args) {
|
|
|
122
109
|
if (!go.ok) {
|
|
123
110
|
console.log('Nothing opened, nothing written.');
|
|
124
111
|
process.exitCode = 1;
|
|
125
|
-
return;
|
|
112
|
+
return { ok: false };
|
|
126
113
|
}
|
|
127
114
|
const { browser, using } = await (0, launcher_1.launchBrowser)({ headed: true });
|
|
128
115
|
console.log(`🌐 Using ${using} (visible window)`);
|
|
@@ -131,26 +118,75 @@ async function runLogin(args) {
|
|
|
131
118
|
const context = await browser.newContext(contextOptions);
|
|
132
119
|
const page = await context.newPage();
|
|
133
120
|
await page.goto(target, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
121
|
+
// Where an UNauthenticated browser lands is the thing a real login has to beat, so it
|
|
122
|
+
// is read before anything else happens.
|
|
123
|
+
const startedAt = page.url();
|
|
124
|
+
console.log(waitSeconds > 0
|
|
125
|
+
? `\n👀 Log in in the browser window. I am watching it — when the session actually opens\n` +
|
|
126
|
+
` the app, I save it and carry on by myself. Nothing to come back and confirm here.\n` +
|
|
127
|
+
` (Press Enter if you would rather stop the wait now.)`
|
|
128
|
+
: `\n👀 Log in in the browser window, then press Enter here.`);
|
|
129
|
+
const enter = waitForEnter();
|
|
130
|
+
let watch;
|
|
131
|
+
try {
|
|
132
|
+
watch =
|
|
133
|
+
waitSeconds > 0
|
|
134
|
+
? await watchForLogin({
|
|
135
|
+
browser,
|
|
136
|
+
context,
|
|
137
|
+
page,
|
|
138
|
+
target,
|
|
139
|
+
startedAt,
|
|
140
|
+
contextOptions,
|
|
141
|
+
confirmed: enter.promise,
|
|
142
|
+
timeoutMs: waitSeconds * 1000,
|
|
143
|
+
})
|
|
144
|
+
: (await enter.promise, { end: 'confirmed' });
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
enter.cancel();
|
|
148
|
+
}
|
|
149
|
+
// A timeout is **not** a refusal. It only means nobody told us the login finished, so
|
|
150
|
+
// the question becomes the one this verb always answers: what does the browser actually
|
|
151
|
+
// hold right now? `sessionDecision` says it honestly — an empty state writes nothing and
|
|
152
|
+
// keeps the file that worked, a wall goes beside it, neither is ever a ✅. Returning
|
|
153
|
+
// early instead would throw away a session somebody really did obtain, just because the
|
|
154
|
+
// watch could not see the app from the outside.
|
|
155
|
+
if (watch.end === 'timeout') {
|
|
156
|
+
console.log(`\n⏳ Nothing that looks like a login happened in ${(0, sessionCheck_2.describeAge)(waitSeconds)}. Checking what the\n` +
|
|
157
|
+
` browser holds anyway — whatever it is, it is reported rather than assumed.`);
|
|
158
|
+
}
|
|
140
159
|
// Taken in memory, written only once it has been replayed. `storageState({ path })`
|
|
141
160
|
// wrote first and asked nothing, so an empty state — an app keeping its token in
|
|
142
161
|
// IndexedDB, or a login that never completed — printed a green "Session saved" *and*
|
|
143
162
|
// overwrote a session that worked. Two failures in one line: a false green, and the
|
|
144
163
|
// destruction of the good file it replaced.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
164
|
+
//
|
|
165
|
+
// A closed window is not a special case: whatever the context held when it went is what
|
|
166
|
+
// gets judged, and `sessionDecision` says the honest thing about it.
|
|
167
|
+
const takenOn = page.isClosed() ? startedAt : page.url();
|
|
168
|
+
// The state and the proof about it travel together — writing a state taken *after* the
|
|
169
|
+
// verification would report a proof about a file that was never the file.
|
|
170
|
+
//
|
|
171
|
+
// Which pair, though, depends on how the watch ended. `verified` is the watch's own, by
|
|
172
|
+
// definition. A **closed** window leaves no way to take another, so the last attempt is
|
|
173
|
+
// all there is. But `confirmed` must be re-checked: pressing Enter means *look now*, and
|
|
174
|
+
// the person very often finishes the login and only then reaches for the keyboard — the
|
|
175
|
+
// last failed attempt would be a verdict about the moment before they succeeded.
|
|
176
|
+
const reusable = watch.end === 'verified' || (watch.end === 'closed' && watch.state);
|
|
177
|
+
let state = reusable ? watch.state : undefined;
|
|
178
|
+
let replay = reusable ? watch.replay : undefined;
|
|
179
|
+
if (!state || !replay) {
|
|
180
|
+
state = await context.storageState().catch(() => ({ cookies: [], origins: [] }));
|
|
181
|
+
console.log(`\n🔁 Checking the session actually holds — reopening ${target} with it, in a clean browser.`);
|
|
182
|
+
replay = await replaySession(browser, state, target, contextOptions);
|
|
183
|
+
}
|
|
149
184
|
const decision = sessionDecision({
|
|
150
185
|
cookies: state.cookies.length,
|
|
151
186
|
origins: state.origins?.length || 0,
|
|
152
187
|
landed: replay.landed,
|
|
153
188
|
wall: replay.wall,
|
|
189
|
+
offSite: replay.offSite,
|
|
154
190
|
error: replay.error,
|
|
155
191
|
file,
|
|
156
192
|
existed: fs.existsSync(file),
|
|
@@ -165,11 +201,37 @@ async function runLogin(args) {
|
|
|
165
201
|
(decision.ok ? console.log : console.error)(line);
|
|
166
202
|
if (!decision.ok)
|
|
167
203
|
process.exitCode = 1;
|
|
204
|
+
return { ok: decision.ok, ...(decision.action !== 'none' ? { file: decision.target } : {}) };
|
|
168
205
|
}
|
|
169
206
|
finally {
|
|
170
207
|
await browser.close();
|
|
171
208
|
}
|
|
172
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* "Press Enter" as a *cancellable* promise, which `prompts` cannot be.
|
|
212
|
+
*
|
|
213
|
+
* The watch and the keyboard race each other, so the loser has to be taken back: a `prompts`
|
|
214
|
+
* question left open keeps stdin resumed and the process alive long after the browser has
|
|
215
|
+
* closed and the session has been written. Raw stdin gives back a `cancel` that removes the
|
|
216
|
+
* listener and pauses the stream, which is the whole reason for not reusing `prompts` here.
|
|
217
|
+
*/
|
|
218
|
+
function waitForEnter() {
|
|
219
|
+
let done = () => { };
|
|
220
|
+
const promise = new Promise((resolve) => {
|
|
221
|
+
done = resolve;
|
|
222
|
+
});
|
|
223
|
+
const onData = () => {
|
|
224
|
+
cancel();
|
|
225
|
+
done();
|
|
226
|
+
};
|
|
227
|
+
const cancel = () => {
|
|
228
|
+
process.stdin.off('data', onData);
|
|
229
|
+
process.stdin.pause();
|
|
230
|
+
};
|
|
231
|
+
process.stdin.resume();
|
|
232
|
+
process.stdin.on('data', onData);
|
|
233
|
+
return { promise, cancel };
|
|
234
|
+
}
|
|
173
235
|
/**
|
|
174
236
|
* `.ia-qa/.gitignore`, scoped to the one file in there that is a secret.
|
|
175
237
|
*
|
|
@@ -203,8 +265,12 @@ function writeSessionGitignore() {
|
|
|
203
265
|
* on screen is logged in. That gap is what produced "✅ Session saved — 0 cookies".
|
|
204
266
|
*
|
|
205
267
|
* A wall is read the way `assertLanded` reads it: sent somewhere else *and* shown a
|
|
206
|
-
* password field
|
|
207
|
-
*
|
|
268
|
+
* password field — asked of every frame, and given a moment to render one. A plain
|
|
269
|
+
* redirect (`/` → `/home`) is not a wall. A navigation that throws is not a verdict
|
|
270
|
+
* either — it is reported as unverified, never as a pass.
|
|
271
|
+
*
|
|
272
|
+
* `offSite` is the third fact, and it is the one that was missing: "no password field"
|
|
273
|
+
* says nothing when the browser is not even on the app any more.
|
|
208
274
|
*/
|
|
209
275
|
async function replaySession(browser, state, target, contextOptions = {}) {
|
|
210
276
|
const context = await browser.newContext({ ...contextOptions, storageState: state });
|
|
@@ -212,8 +278,10 @@ async function replaySession(browser, state, target, contextOptions = {}) {
|
|
|
212
278
|
const page = await context.newPage();
|
|
213
279
|
await page.goto(target, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
|
214
280
|
const landed = page.url();
|
|
215
|
-
const
|
|
216
|
-
|
|
281
|
+
const elsewhere = !(0, map_1.samePath)(landed, target);
|
|
282
|
+
const wall = elsewhere && (await (0, map_1.hasPasswordField)(page, 3000));
|
|
283
|
+
const offSite = !(0, sessionCheck_1.sameSite)(landed, target);
|
|
284
|
+
return { landed, wall, ...(offSite ? { offSite: true } : {}) };
|
|
217
285
|
}
|
|
218
286
|
catch (err) {
|
|
219
287
|
return { landed: '', wall: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -222,6 +290,117 @@ async function replaySession(browser, state, target, contextOptions = {}) {
|
|
|
222
290
|
await context.close();
|
|
223
291
|
}
|
|
224
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Is this state, replayed in a clean context, evidence that somebody logged in?
|
|
295
|
+
*
|
|
296
|
+
* One rule, and it is the definition rather than a heuristic: **a login worked when a clean
|
|
297
|
+
* context reaches somewhere the unauthenticated browser could not.** `startedAt` is where
|
|
298
|
+
* the very first navigation landed — the wall — so a replay that lands back there gained
|
|
299
|
+
* nothing, whatever it shows.
|
|
300
|
+
*
|
|
301
|
+
* Reading "no password field" as success instead is the trap this avoids, and it is not
|
|
302
|
+
* theoretical: a magic-link or passkey login page has no password field at all, so the
|
|
303
|
+
* cheap signal says *logged in* from the first second, and the watch would save a state
|
|
304
|
+
* that opens nothing and call it ✅ — the exact false green this verb was rewritten to stop.
|
|
305
|
+
*/
|
|
306
|
+
function loginProven(replay, startedAt) {
|
|
307
|
+
if (replay.error || replay.wall || replay.offSite)
|
|
308
|
+
return false;
|
|
309
|
+
if (!replay.landed)
|
|
310
|
+
return false;
|
|
311
|
+
return !(0, map_1.samePath)(replay.landed, startedAt);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Watch a visible browser until the person is logged in — instead of asking them to say so.
|
|
315
|
+
*
|
|
316
|
+
* The prompt this replaces was the last place in the verb that trusted a human's word, and
|
|
317
|
+
* the verb's whole history is about not doing that: `storageState({ path })` wrote on that
|
|
318
|
+
* word and printed `✅ Session saved — 0 cookies` over a file that logged nobody in. Asking
|
|
319
|
+
* "are you done?" has the same shape — it is a question about the world answered by
|
|
320
|
+
* someone who is looking at a browser, not at the cookie jar. And it fails in the most
|
|
321
|
+
* ordinary way there is: the person logs in, goes back to what they were doing, and the
|
|
322
|
+
* terminal sits on an unanswered prompt. It was reported by the person who wrote it.
|
|
323
|
+
*
|
|
324
|
+
* So the watch asks the browser, on a loop, and the evidence it accepts is the one the rest
|
|
325
|
+
* of this file already accepts: a **clean context** carrying only the captured state,
|
|
326
|
+
* reaching somewhere the unauthenticated browser could not (`loginProven`).
|
|
327
|
+
*
|
|
328
|
+
* Three restraints:
|
|
329
|
+
*
|
|
330
|
+
* - **It never gives up early.** A verification that fails means *not yet* — a multi-step
|
|
331
|
+
* login, a consent screen still to come — never *this will not work*. After a few failed
|
|
332
|
+
* attempts it explains the one case that genuinely cannot succeed (a token in IndexedDB
|
|
333
|
+
* leaves a session file empty) and keeps watching, because the alternative is telling
|
|
334
|
+
* somebody their login failed while they are still typing.
|
|
335
|
+
* - **The keyboard still works.** `confirmed` is the escape hatch for every app whose
|
|
336
|
+
* login this cannot observe, and whichever finishes first wins. Nothing that worked
|
|
337
|
+
* before stops working.
|
|
338
|
+
* - **Replaying is not free**, so the cheap gate runs first: on the app's own site, no
|
|
339
|
+
* password field in any frame, and something in the cookie jar. A clean context is
|
|
340
|
+
* opened only once that holds, and then only when the live page has moved again or
|
|
341
|
+
* enough time has passed.
|
|
342
|
+
*/
|
|
343
|
+
async function watchForLogin(opts) {
|
|
344
|
+
const note = opts.note ?? ((line) => console.log(line));
|
|
345
|
+
const now = opts.now ?? Date.now;
|
|
346
|
+
const deadline = now() + (opts.timeoutMs ?? 10 * 60 * 1000);
|
|
347
|
+
let confirmed = false;
|
|
348
|
+
void opts.confirmed.then(() => {
|
|
349
|
+
confirmed = true;
|
|
350
|
+
});
|
|
351
|
+
let lastVerifiedUrl = '';
|
|
352
|
+
let lastVerifiedAt = 0;
|
|
353
|
+
let attempts = 0;
|
|
354
|
+
// The last attempt, kept as a pair. It is offered on a `confirmed`/`closed` ending only
|
|
355
|
+
// because it is a true statement about a state we still hold; it is never a success.
|
|
356
|
+
let last;
|
|
357
|
+
for (;;) {
|
|
358
|
+
if (confirmed)
|
|
359
|
+
return { end: 'confirmed', ...last };
|
|
360
|
+
if (opts.page.isClosed())
|
|
361
|
+
return { end: 'closed', ...last };
|
|
362
|
+
if (now() >= deadline)
|
|
363
|
+
return { end: 'timeout', ...last };
|
|
364
|
+
let url = '';
|
|
365
|
+
let cookies = 0;
|
|
366
|
+
try {
|
|
367
|
+
url = opts.page.url();
|
|
368
|
+
cookies = (await opts.context.cookies()).length;
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
// The context went away under us — the person closed the window.
|
|
372
|
+
return { end: 'closed', ...last };
|
|
373
|
+
}
|
|
374
|
+
const worthVerifying = cookies > 0 &&
|
|
375
|
+
(0, sessionCheck_1.sameSite)(url, opts.target) &&
|
|
376
|
+
!(0, map_1.samePath)(url, opts.startedAt) &&
|
|
377
|
+
!(await (0, map_1.hasPasswordField)(opts.page, 0)) &&
|
|
378
|
+
(url !== lastVerifiedUrl || now() - lastVerifiedAt > 8000);
|
|
379
|
+
if (worthVerifying) {
|
|
380
|
+
lastVerifiedUrl = url;
|
|
381
|
+
lastVerifiedAt = now();
|
|
382
|
+
attempts++;
|
|
383
|
+
let state;
|
|
384
|
+
try {
|
|
385
|
+
state = await opts.context.storageState();
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
return { end: 'closed', ...last };
|
|
389
|
+
}
|
|
390
|
+
const replay = await replaySession(opts.browser, state, opts.target, opts.contextOptions ?? {});
|
|
391
|
+
last = { replay, state };
|
|
392
|
+
if (loginProven(replay, opts.startedAt))
|
|
393
|
+
return { end: 'verified', replay, state };
|
|
394
|
+
if (attempts === 3) {
|
|
395
|
+
note(`\n ⏳ You look logged in, but the session I can carry does not open the app on its own yet.\n` +
|
|
396
|
+
` Still watching — a multi-step login is normal here. If your app keeps its token in\n` +
|
|
397
|
+
` IndexedDB, in memory or in a service worker, a session file can never hold it:\n` +
|
|
398
|
+
` press Enter and I will tell you exactly that instead of waiting.`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
await new Promise((r) => setTimeout(r, 1200));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
225
404
|
/**
|
|
226
405
|
* What to do with a session that has just been replayed — and above all, what never to do.
|
|
227
406
|
*
|
|
@@ -272,6 +451,28 @@ function sessionDecision(v) {
|
|
|
272
451
|
].filter(Boolean),
|
|
273
452
|
};
|
|
274
453
|
}
|
|
454
|
+
// A check that landed somewhere else is a check that did not run, and the doctrine for
|
|
455
|
+
// those is already written above: quarantine, never over the existing file. It reaches
|
|
456
|
+
// here rather than into the branch above because nothing about it says "wall" — the
|
|
457
|
+
// honest sentence is "the app was not reached", and it must not be printed as a pass.
|
|
458
|
+
if (v.offSite) {
|
|
459
|
+
const quarantine = `${v.file}.unverified`;
|
|
460
|
+
return {
|
|
461
|
+
ok: false,
|
|
462
|
+
action: v.existed ? 'quarantine' : 'write',
|
|
463
|
+
target: v.existed ? quarantine : v.file,
|
|
464
|
+
lines: [
|
|
465
|
+
`\n⚠ Saved ${held}, but they could not be checked — reopening the app with them landed on`,
|
|
466
|
+
` ${v.landed}, which is another site. No login form was found there, and that proves`,
|
|
467
|
+
` nothing: an identity provider asking for an email first looks exactly like this.`,
|
|
468
|
+
v.existed
|
|
469
|
+
? ` ${v.file} was left untouched. The new state is beside it:\n ${quarantine}\n` +
|
|
470
|
+
` Open that URL yourself: if it is your app, rename the file over the other one.`
|
|
471
|
+
: ` Written anyway, so your login is not lost:\n ${v.file}\n` +
|
|
472
|
+
` Open that URL yourself to see whether it is your app or its login provider.`,
|
|
473
|
+
],
|
|
474
|
+
};
|
|
475
|
+
}
|
|
275
476
|
return {
|
|
276
477
|
ok: true,
|
|
277
478
|
action: 'write',
|
package/dist/cli/login.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/cli/login.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/cli/login.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/map.d.ts
CHANGED
|
@@ -112,6 +112,27 @@ export declare function runSteps(page: Page, target: PageTarget): Promise<void>;
|
|
|
112
112
|
* only the app can mint, and is therefore not a URL anyone can navigate to twice.
|
|
113
113
|
*/
|
|
114
114
|
export declare function suggestAuthBlock(page: Page, loginUrl?: string): Promise<string | null>;
|
|
115
|
+
/**
|
|
116
|
+
* Is a password field on screen — in any frame, and after the page has had a moment to draw one?
|
|
117
|
+
*
|
|
118
|
+
* Both halves were false negatives in the field, and a false negative here is the worst
|
|
119
|
+
* answer this package gives: it is what lets a contract named `dashboard` describe a login
|
|
120
|
+
* form, and what let `session --check` print `✅ This session opens the app` beside an
|
|
121
|
+
* identity provider's own URL.
|
|
122
|
+
*
|
|
123
|
+
* - **The wait.** `domcontentloaded` then an instant `count()` asks an SPA for a form it
|
|
124
|
+
* has not rendered yet. Nothing about the answer was wrong except when it was asked.
|
|
125
|
+
* - **The frames.** `page.locator` searches the main frame only, and a federated login is
|
|
126
|
+
* very often in an iframe. Asking the main frame alone is asking the wrong document.
|
|
127
|
+
*
|
|
128
|
+
* Both callers reach this only after a redirect — a page that landed where it was asked
|
|
129
|
+
* never gets here — so a healthy run pays nothing, and only an already abnormal page pays.
|
|
130
|
+
* The wait is short by default for the case that *is* common on a healthy app (a declared
|
|
131
|
+
* page that moved, with no login anywhere): one redirected page must not cost three
|
|
132
|
+
* seconds twenty times over. `session --check` raises it, because there the whole verb is
|
|
133
|
+
* this one question and there is exactly one page to ask it of.
|
|
134
|
+
*/
|
|
135
|
+
export declare function hasPasswordField(page: Page, timeoutMs?: number): Promise<boolean>;
|
|
115
136
|
/** Compare by pathname alone: a query string or a trailing slash is not another page. */
|
|
116
137
|
export declare function samePath(a: string, b: string): boolean;
|
|
117
138
|
/**
|
package/dist/cli/map.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.LoginWallError = void 0;
|
|
|
37
37
|
exports.runMap = runMap;
|
|
38
38
|
exports.runSteps = runSteps;
|
|
39
39
|
exports.suggestAuthBlock = suggestAuthBlock;
|
|
40
|
+
exports.hasPasswordField = hasPasswordField;
|
|
40
41
|
exports.samePath = samePath;
|
|
41
42
|
exports.assertLanded = assertLanded;
|
|
42
43
|
exports.login = login;
|
|
@@ -1186,6 +1187,50 @@ async function suggestAuthBlock(page, loginUrl) {
|
|
|
1186
1187
|
};
|
|
1187
1188
|
return JSON.stringify(block, null, 2);
|
|
1188
1189
|
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Is a password field on screen — in any frame, and after the page has had a moment to draw one?
|
|
1192
|
+
*
|
|
1193
|
+
* Both halves were false negatives in the field, and a false negative here is the worst
|
|
1194
|
+
* answer this package gives: it is what lets a contract named `dashboard` describe a login
|
|
1195
|
+
* form, and what let `session --check` print `✅ This session opens the app` beside an
|
|
1196
|
+
* identity provider's own URL.
|
|
1197
|
+
*
|
|
1198
|
+
* - **The wait.** `domcontentloaded` then an instant `count()` asks an SPA for a form it
|
|
1199
|
+
* has not rendered yet. Nothing about the answer was wrong except when it was asked.
|
|
1200
|
+
* - **The frames.** `page.locator` searches the main frame only, and a federated login is
|
|
1201
|
+
* very often in an iframe. Asking the main frame alone is asking the wrong document.
|
|
1202
|
+
*
|
|
1203
|
+
* Both callers reach this only after a redirect — a page that landed where it was asked
|
|
1204
|
+
* never gets here — so a healthy run pays nothing, and only an already abnormal page pays.
|
|
1205
|
+
* The wait is short by default for the case that *is* common on a healthy app (a declared
|
|
1206
|
+
* page that moved, with no login anywhere): one redirected page must not cost three
|
|
1207
|
+
* seconds twenty times over. `session --check` raises it, because there the whole verb is
|
|
1208
|
+
* this one question and there is exactly one page to ask it of.
|
|
1209
|
+
*/
|
|
1210
|
+
async function hasPasswordField(page, timeoutMs = 1500) {
|
|
1211
|
+
const deadline = Date.now() + timeoutMs;
|
|
1212
|
+
for (;;) {
|
|
1213
|
+
if (page.isClosed())
|
|
1214
|
+
return false;
|
|
1215
|
+
for (const frame of page.frames()) {
|
|
1216
|
+
try {
|
|
1217
|
+
if ((await frame.locator('input[type="password"]').count()) > 0)
|
|
1218
|
+
return true;
|
|
1219
|
+
}
|
|
1220
|
+
catch {
|
|
1221
|
+
/* a frame that navigated mid-check answers nothing; the next pass asks again */
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
if (Date.now() >= deadline)
|
|
1225
|
+
return false;
|
|
1226
|
+
try {
|
|
1227
|
+
await page.waitForTimeout(200);
|
|
1228
|
+
}
|
|
1229
|
+
catch {
|
|
1230
|
+
return false;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1189
1234
|
/** Compare by pathname alone: a query string or a trailing slash is not another page. */
|
|
1190
1235
|
function samePath(a, b) {
|
|
1191
1236
|
const pathnameOf = (u) => {
|
|
@@ -1221,7 +1266,7 @@ async function assertLanded(page, target, wanted, config, session) {
|
|
|
1221
1266
|
const landed = page.url();
|
|
1222
1267
|
if (samePath(landed, wanted))
|
|
1223
1268
|
return {};
|
|
1224
|
-
if ((await page
|
|
1269
|
+
if (!(await hasPasswordField(page))) {
|
|
1225
1270
|
console.warn(` ⚠ Redirected to ${landed} — this contract describes that page, not ${wanted}.`);
|
|
1226
1271
|
// Returned as well as printed. Field report: this line appeared once, at line 37 of a
|
|
1227
1272
|
// 172-line log, in the middle of the mapping stream — and it was the only fact that
|