iframer-toolkit 0.11.1 → 0.11.2
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 +2 -0
- package/dist/cli.js +413 -31
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
- package/skills/iframer.md +2 -0
package/README.md
CHANGED
|
@@ -268,6 +268,7 @@ Setup:
|
|
|
268
268
|
extension path Print the extension folder to load in chrome://extensions
|
|
269
269
|
extension token Print the pairing token (only needed to pair by hand)
|
|
270
270
|
update [--check] Update iframer via npm (--check: report only)
|
|
271
|
+
refresh Rewrite the in-page kit and the /iframer skill (update runs it)
|
|
271
272
|
remove [chromium|mcp|extension] Remove everything, or one piece
|
|
272
273
|
|
|
273
274
|
Browser:
|
|
@@ -344,6 +345,7 @@ iframer ingest --latest --typed # finds the capture in T3's downloads -> ./<host
|
|
|
344
345
|
- **Identity, without the values:** the kit learns which auth headers the page sends and where it keeps each value (a localStorage key, a field inside it, a cookie, or page memory). api.json records that under `identity`, and `__ifk.call` rebuilds the headers from there for every replay, so the generated client acts as the logged-in user.
|
|
345
346
|
- **Output:** `api.json` (endpoint map with coverage), `client.js` (in-page client over `window.__ifk.call`), `client.d.ts` with `--typed`, and the domain's knowledge file.
|
|
346
347
|
- **Replay stays in the page:** HttpOnly cookies are invisible to page JS, so generated calls run through the tab. Same-origin only, GET unless approved, 50 calls per session, and a hard stop on 403, 429 or a captcha page.
|
|
348
|
+
- **Next.js:** dynamic route segments are named from the app's own route data, so `/wichitalife/dashboard` is saved as `/{communitySlug}/dashboard` and the client takes `communitySlug` as an argument. RSC responses (`text/x-component`) become data: the props the server passed to each client component, or a Server Action's return value. Server Actions are saved by export name and called with `window.__ifk.action(name, [args], {approved:true})`. The id is looked up again in the page's JS on every call, so it keeps working after a deploy.
|
|
347
349
|
- **Not complete:** worker requests, redirect hops, cross-origin iframes and Set-Cookie are invisible to the kit, and every report says so.
|
|
348
350
|
- **Survives the page:** after a full page load the agent re-runs one short `eval` and the capture continues. Works on sites that delete `window.sessionStorage` (Discord).
|
|
349
351
|
- **Other tools:** `--for playwright-mcp` or `--for any`. Tools without file upload can evaluate `iframer kit --print` instead, which costs about 17 KB per page load.
|
package/dist/cli.js
CHANGED
|
@@ -11621,10 +11621,12 @@ function pageKit(opts) {
|
|
|
11621
11621
|
var w = window;
|
|
11622
11622
|
var VERSION = 1;
|
|
11623
11623
|
var KEY = "__ifk_state";
|
|
11624
|
-
|
|
11624
|
+
var BUILD = opts.build || "dev";
|
|
11625
|
+
if (w.__ifk && w.__ifk.version === VERSION && w.__ifk.build === BUILD) {
|
|
11625
11626
|
var s0 = w.__ifk.summary();
|
|
11626
11627
|
return { installed: true, again: true, version: VERSION, seen: s0.seen };
|
|
11627
11628
|
}
|
|
11629
|
+
var replaced = null;
|
|
11628
11630
|
var mode = opts.mode === "passive" ? "passive" : "hook";
|
|
11629
11631
|
var hostRe = null;
|
|
11630
11632
|
try {
|
|
@@ -11652,6 +11654,20 @@ function pageKit(opts) {
|
|
|
11652
11654
|
}
|
|
11653
11655
|
}
|
|
11654
11656
|
var ss = store();
|
|
11657
|
+
if (w.__ifk && w.__ifk.stop) {
|
|
11658
|
+
replaced = w.__ifk.build || "old";
|
|
11659
|
+
try {
|
|
11660
|
+
var keep = {};
|
|
11661
|
+
[KEY, "__ifk", "__ifkApi"].forEach(function(k) {
|
|
11662
|
+
keep[k] = ss && ss.getItem(k);
|
|
11663
|
+
});
|
|
11664
|
+
w.__ifk.stop();
|
|
11665
|
+
Object.keys(keep).forEach(function(k) {
|
|
11666
|
+
if (keep[k] != null) ss.setItem(k, keep[k]);
|
|
11667
|
+
});
|
|
11668
|
+
} catch (e) {
|
|
11669
|
+
}
|
|
11670
|
+
}
|
|
11655
11671
|
function lstore() {
|
|
11656
11672
|
try {
|
|
11657
11673
|
if (w.localStorage) return w.localStorage;
|
|
@@ -11669,6 +11685,8 @@ function pageKit(opts) {
|
|
|
11669
11685
|
var budget = { calls: 0, stopped: null };
|
|
11670
11686
|
var authSrc = {};
|
|
11671
11687
|
var authMem = {};
|
|
11688
|
+
var rp = {};
|
|
11689
|
+
var acts = {};
|
|
11672
11690
|
var restored = 0;
|
|
11673
11691
|
try {
|
|
11674
11692
|
var saved = JSON.parse(ss.getItem(KEY) || "null");
|
|
@@ -11677,6 +11695,7 @@ function pageKit(opts) {
|
|
|
11677
11695
|
po = saved.po || {};
|
|
11678
11696
|
if (saved.budget) budget = saved.budget;
|
|
11679
11697
|
if (saved.authSrc) authSrc = saved.authSrc;
|
|
11698
|
+
if (saved.acts) acts = saved.acts;
|
|
11680
11699
|
restored = Object.keys(m).length;
|
|
11681
11700
|
}
|
|
11682
11701
|
} catch (e) {
|
|
@@ -11702,9 +11721,222 @@ function pageKit(opts) {
|
|
|
11702
11721
|
}
|
|
11703
11722
|
function tpl(p) {
|
|
11704
11723
|
return p.split("/").map(function(s) {
|
|
11724
|
+
var d = s;
|
|
11725
|
+
try {
|
|
11726
|
+
d = decodeURIComponent(s);
|
|
11727
|
+
} catch (e) {
|
|
11728
|
+
}
|
|
11729
|
+
if (s && rp.hasOwnProperty(d)) return "{" + rp[d] + "}";
|
|
11705
11730
|
return isValueLike(s) ? "{id}" : s;
|
|
11706
11731
|
}).join("/");
|
|
11707
11732
|
}
|
|
11733
|
+
var PARAM_RE = /\["([A-Za-z_][\w-]{0,40})","([^"\\\/]{1,80})","(?:d|c|oc|ci|dc)"/g;
|
|
11734
|
+
function learnParams(text) {
|
|
11735
|
+
if (!text || text.indexOf('","d"') < 0 && text.indexOf('","c"') < 0 && text.indexOf('","oc"') < 0) return;
|
|
11736
|
+
PARAM_RE.lastIndex = 0;
|
|
11737
|
+
var x;
|
|
11738
|
+
while (x = PARAM_RE.exec(text)) if (!rp.hasOwnProperty(x[2])) rp[x[2]] = x[1];
|
|
11739
|
+
}
|
|
11740
|
+
var nextIdx = 0;
|
|
11741
|
+
function scanNext() {
|
|
11742
|
+
try {
|
|
11743
|
+
var f = w.__next_f;
|
|
11744
|
+
if (f && f.length > nextIdx) {
|
|
11745
|
+
for (var i = nextIdx; i < f.length; i++) if (f[i] && typeof f[i][1] === "string") learnParams(f[i][1]);
|
|
11746
|
+
nextIdx = f.length;
|
|
11747
|
+
}
|
|
11748
|
+
} catch (e) {
|
|
11749
|
+
}
|
|
11750
|
+
try {
|
|
11751
|
+
var q = w.__NEXT_DATA__ && w.__NEXT_DATA__.query;
|
|
11752
|
+
if (q) Object.keys(q).forEach(function(k) {
|
|
11753
|
+
if (typeof q[k] === "string" && !rp.hasOwnProperty(q[k])) rp[q[k]] = k;
|
|
11754
|
+
});
|
|
11755
|
+
} catch (e) {
|
|
11756
|
+
}
|
|
11757
|
+
}
|
|
11758
|
+
function learnFromBody(b, u) {
|
|
11759
|
+
if (typeof b !== "string" || b.length > 2e4) return;
|
|
11760
|
+
var j;
|
|
11761
|
+
try {
|
|
11762
|
+
j = JSON.parse(b);
|
|
11763
|
+
} catch (e) {
|
|
11764
|
+
return;
|
|
11765
|
+
}
|
|
11766
|
+
var segs = {};
|
|
11767
|
+
(u.pathname + "/" + location.pathname).split("/").forEach(function(s) {
|
|
11768
|
+
if (s) {
|
|
11769
|
+
try {
|
|
11770
|
+
segs[decodeURIComponent(s)] = 1;
|
|
11771
|
+
} catch (e) {
|
|
11772
|
+
}
|
|
11773
|
+
}
|
|
11774
|
+
});
|
|
11775
|
+
(function walk(v, d) {
|
|
11776
|
+
if (!v || typeof v !== "object" || d > 3) return;
|
|
11777
|
+
Object.keys(v).forEach(function(k) {
|
|
11778
|
+
var x = v[k];
|
|
11779
|
+
if (typeof x === "string" && x.length > 1 && segs[x] && /(id|slug|key|handle|code|name)$/i.test(k) && !rp.hasOwnProperty(x)) rp[x] = k;
|
|
11780
|
+
else walk(x, d + 1);
|
|
11781
|
+
});
|
|
11782
|
+
})(j, 0);
|
|
11783
|
+
}
|
|
11784
|
+
function parseFlight(t) {
|
|
11785
|
+
var rows = {};
|
|
11786
|
+
try {
|
|
11787
|
+
var b = new TextEncoder().encode(t), dec = new TextDecoder(), i = 0, n = b.length;
|
|
11788
|
+
while (i < n) {
|
|
11789
|
+
var c = i;
|
|
11790
|
+
while (c < n && b[c] !== 58 && b[c] !== 10) c++;
|
|
11791
|
+
if (c >= n) break;
|
|
11792
|
+
if (b[c] === 10) {
|
|
11793
|
+
i = c + 1;
|
|
11794
|
+
continue;
|
|
11795
|
+
}
|
|
11796
|
+
var id = dec.decode(b.subarray(i, c));
|
|
11797
|
+
var j = c + 1;
|
|
11798
|
+
if (b[j] === 84) {
|
|
11799
|
+
var cm = j + 1;
|
|
11800
|
+
while (cm < n && b[cm] !== 44) cm++;
|
|
11801
|
+
var len = parseInt(dec.decode(b.subarray(j + 1, cm)), 16) || 0;
|
|
11802
|
+
rows[id] = { text: dec.decode(b.subarray(cm + 1, cm + 1 + len)) };
|
|
11803
|
+
i = cm + 1 + len;
|
|
11804
|
+
if (b[i] === 10) i++;
|
|
11805
|
+
continue;
|
|
11806
|
+
}
|
|
11807
|
+
var nl = j;
|
|
11808
|
+
while (nl < n && b[nl] !== 10) nl++;
|
|
11809
|
+
var p = dec.decode(b.subarray(j, nl));
|
|
11810
|
+
i = nl + 1;
|
|
11811
|
+
if (!/^[0-9a-f]+$/i.test(id)) continue;
|
|
11812
|
+
var tag = (/^[A-Z]+/.exec(p) || [""])[0];
|
|
11813
|
+
try {
|
|
11814
|
+
rows[id] = { tag, v: JSON.parse(p.slice(tag.length)) };
|
|
11815
|
+
} catch (e) {
|
|
11816
|
+
}
|
|
11817
|
+
}
|
|
11818
|
+
} catch (e) {
|
|
11819
|
+
}
|
|
11820
|
+
return rows;
|
|
11821
|
+
}
|
|
11822
|
+
function flightData(rows) {
|
|
11823
|
+
function resolve(v, d) {
|
|
11824
|
+
if (d > 30) return null;
|
|
11825
|
+
if (typeof v === "string") {
|
|
11826
|
+
if (v === "$undefined") return void 0;
|
|
11827
|
+
if (/^\$D/.test(v)) return v.slice(2);
|
|
11828
|
+
var r = /^\$@?([0-9a-f]+)$/.exec(v);
|
|
11829
|
+
if (r && rows[r[1]] && rows[r[1]].v !== void 0) return resolve(rows[r[1]].v, d + 1);
|
|
11830
|
+
if (r && rows[r[1]] && rows[r[1]].text !== void 0) return rows[r[1]].text;
|
|
11831
|
+
return v.charAt(0) === "$" && v.charAt(1) === "$" ? v.slice(1) : v;
|
|
11832
|
+
}
|
|
11833
|
+
if (Array.isArray(v)) return v.map(function(x) {
|
|
11834
|
+
return resolve(x, d + 1);
|
|
11835
|
+
});
|
|
11836
|
+
if (v && typeof v === "object") {
|
|
11837
|
+
var o = {};
|
|
11838
|
+
Object.keys(v).forEach(function(k) {
|
|
11839
|
+
o[k] = resolve(v[k], d + 1);
|
|
11840
|
+
});
|
|
11841
|
+
return o;
|
|
11842
|
+
}
|
|
11843
|
+
return v;
|
|
11844
|
+
}
|
|
11845
|
+
var r0 = rows["0"] && rows["0"].v;
|
|
11846
|
+
if (r0 && typeof r0 === "object" && !Array.isArray(r0) && typeof r0.a === "string" && /^\$@/.test(r0.a)) {
|
|
11847
|
+
return { action: resolve(r0.a, 0) };
|
|
11848
|
+
}
|
|
11849
|
+
var names = {};
|
|
11850
|
+
Object.keys(rows).forEach(function(id) {
|
|
11851
|
+
var r = rows[id];
|
|
11852
|
+
if (r.tag !== "I" || !r.v) return;
|
|
11853
|
+
var nm = Array.isArray(r.v) ? r.v[2] : r.v.name;
|
|
11854
|
+
names["$L" + id] = nm && nm !== "default" && nm !== "*" ? nm : "Component" + id;
|
|
11855
|
+
});
|
|
11856
|
+
var comps = {}, seen = 0;
|
|
11857
|
+
function walk(v, d) {
|
|
11858
|
+
if (d > 60 || seen > 400 || !v || typeof v !== "object") return;
|
|
11859
|
+
if (Array.isArray(v)) {
|
|
11860
|
+
if (v[0] === "$" && typeof v[1] === "string" && v.length === 4) {
|
|
11861
|
+
var props = v[3];
|
|
11862
|
+
if (props && typeof props === "object" && names[v[1]]) {
|
|
11863
|
+
var data = {}, has = false;
|
|
11864
|
+
Object.keys(props).forEach(function(k) {
|
|
11865
|
+
if (k !== "children") {
|
|
11866
|
+
data[k] = resolve(props[k], 0);
|
|
11867
|
+
has = true;
|
|
11868
|
+
}
|
|
11869
|
+
});
|
|
11870
|
+
if (has) {
|
|
11871
|
+
seen++;
|
|
11872
|
+
(comps[names[v[1]]] = comps[names[v[1]]] || []).push(data);
|
|
11873
|
+
}
|
|
11874
|
+
}
|
|
11875
|
+
if (props && typeof props === "object") walk(props.children, d + 1);
|
|
11876
|
+
return;
|
|
11877
|
+
}
|
|
11878
|
+
v.forEach(function(x) {
|
|
11879
|
+
walk(x, d + 1);
|
|
11880
|
+
});
|
|
11881
|
+
return;
|
|
11882
|
+
}
|
|
11883
|
+
Object.keys(v).forEach(function(k) {
|
|
11884
|
+
walk(v[k], d + 1);
|
|
11885
|
+
});
|
|
11886
|
+
}
|
|
11887
|
+
Object.keys(rows).forEach(function(id) {
|
|
11888
|
+
if (!rows[id].tag && rows[id].v !== void 0) walk(rows[id].v, 0);
|
|
11889
|
+
});
|
|
11890
|
+
Object.keys(comps).forEach(function(k) {
|
|
11891
|
+
if (comps[k].length === 1) comps[k] = comps[k][0];
|
|
11892
|
+
});
|
|
11893
|
+
return { components: comps };
|
|
11894
|
+
}
|
|
11895
|
+
function isFlight(ct) {
|
|
11896
|
+
return /text\/x-component/i.test(ct || "");
|
|
11897
|
+
}
|
|
11898
|
+
var ACT_RE = /\(\s*"([0-9a-f]{40,44})"\s*,[^"()]{0,200}?,\s*"([A-Za-z_$][\w$]*)"\s*\)/g;
|
|
11899
|
+
var actScan = null;
|
|
11900
|
+
function scanActions(fresh) {
|
|
11901
|
+
if (actScan && !fresh) return actScan;
|
|
11902
|
+
var srcs = {};
|
|
11903
|
+
try {
|
|
11904
|
+
[].slice.call(document.scripts).forEach(function(sc) {
|
|
11905
|
+
if (sc.src) srcs[sc.src] = 1;
|
|
11906
|
+
});
|
|
11907
|
+
} catch (e) {
|
|
11908
|
+
}
|
|
11909
|
+
try {
|
|
11910
|
+
performance.getEntriesByType("resource").forEach(function(r) {
|
|
11911
|
+
if (r.initiatorType === "script" || /\.js(\?|$)/.test(r.name)) srcs[r.name] = 1;
|
|
11912
|
+
});
|
|
11913
|
+
} catch (e) {
|
|
11914
|
+
}
|
|
11915
|
+
var found = {};
|
|
11916
|
+
actScan = Promise.all(Object.keys(srcs).filter(function(s) {
|
|
11917
|
+
var u = abs(s);
|
|
11918
|
+
return u && u.origin === location.origin && /\/_next\//.test(u.pathname);
|
|
11919
|
+
}).map(function(s) {
|
|
11920
|
+
return origFetch.call(w, s, { cache: fresh ? "no-cache" : "default" }).then(function(r) {
|
|
11921
|
+
return r.text();
|
|
11922
|
+
}).then(function(t) {
|
|
11923
|
+
ACT_RE.lastIndex = 0;
|
|
11924
|
+
var x;
|
|
11925
|
+
while (x = ACT_RE.exec(t)) {
|
|
11926
|
+
found[x[1]] = x[2];
|
|
11927
|
+
acts[x[1]] = x[2];
|
|
11928
|
+
}
|
|
11929
|
+
}, function() {
|
|
11930
|
+
});
|
|
11931
|
+
})).then(function() {
|
|
11932
|
+
persistSoon();
|
|
11933
|
+
return found;
|
|
11934
|
+
});
|
|
11935
|
+
return actScan;
|
|
11936
|
+
}
|
|
11937
|
+
function actionLabel(id) {
|
|
11938
|
+
return acts[id] || id.slice(0, 8);
|
|
11939
|
+
}
|
|
11708
11940
|
function abs(u, base) {
|
|
11709
11941
|
try {
|
|
11710
11942
|
return new URL(String(u), base || location.href);
|
|
@@ -11768,6 +12000,13 @@ function pageKit(opts) {
|
|
|
11768
12000
|
} catch (e) {
|
|
11769
12001
|
}
|
|
11770
12002
|
}
|
|
12003
|
+
if (isFlight(ct)) {
|
|
12004
|
+
learnParams(text);
|
|
12005
|
+
try {
|
|
12006
|
+
return { rsc: shape(flightData(parseFlight(text))) };
|
|
12007
|
+
} catch (e) {
|
|
12008
|
+
}
|
|
12009
|
+
}
|
|
11771
12010
|
return { contentType: (ct || "").split(";")[0], bytes: text.length };
|
|
11772
12011
|
}
|
|
11773
12012
|
function cap(t) {
|
|
@@ -11776,7 +12015,7 @@ function pageKit(opts) {
|
|
|
11776
12015
|
var persistTimer = null;
|
|
11777
12016
|
function persist() {
|
|
11778
12017
|
try {
|
|
11779
|
-
ss.setItem(KEY, JSON.stringify({ v: VERSION, m, po, budget, authSrc }));
|
|
12018
|
+
ss.setItem(KEY, JSON.stringify({ v: VERSION, m, po, budget, authSrc, acts }));
|
|
11780
12019
|
} catch (e) {
|
|
11781
12020
|
}
|
|
11782
12021
|
}
|
|
@@ -11873,8 +12112,19 @@ function pageKit(opts) {
|
|
|
11873
12112
|
var u = abs(url, base);
|
|
11874
12113
|
if (!u || !/^https?:$/.test(u.protocol)) return;
|
|
11875
12114
|
if (hostRe && !hostRe.test(u.host)) return;
|
|
11876
|
-
|
|
12115
|
+
scanNext();
|
|
12116
|
+
if (text !== void 0 && isFlight(ct)) learnParams(text);
|
|
12117
|
+
learnFromBody(body, u);
|
|
12118
|
+
var act = null;
|
|
12119
|
+
Object.keys(headers || {}).forEach(function(h) {
|
|
12120
|
+
if (h.toLowerCase() === "next-action" && typeof headers[h] === "string") act = headers[h];
|
|
12121
|
+
});
|
|
12122
|
+
var k = String(method || "GET").toUpperCase() + " " + u.origin + tpl(u.pathname) + (act ? "#" + actionLabel(act) : "");
|
|
11877
12123
|
var e = m[k] || (m[k] = { kind, n: 0, status: [], query: [], headers: [], auth: [] });
|
|
12124
|
+
if (act) {
|
|
12125
|
+
e.next = { action: act };
|
|
12126
|
+
if (!acts[act]) scanActions(true);
|
|
12127
|
+
}
|
|
11878
12128
|
e.n++;
|
|
11879
12129
|
if (status != null && e.status.indexOf(status) < 0) e.status.push(status);
|
|
11880
12130
|
u.searchParams.forEach(function(_, q) {
|
|
@@ -11935,18 +12185,18 @@ function pageKit(opts) {
|
|
|
11935
12185
|
var hs = {};
|
|
11936
12186
|
var src = init && init.headers || isReq && input.headers;
|
|
11937
12187
|
if (src) new win.Headers(src).forEach(function(v, k) {
|
|
11938
|
-
hs[k] = AUTH_H.test(k) ? v : 1;
|
|
12188
|
+
hs[k] = AUTH_H.test(k) || /^next-action$/i.test(k) ? v : 1;
|
|
11939
12189
|
});
|
|
11940
12190
|
var b = init && init.body;
|
|
11941
12191
|
var at = base();
|
|
11942
|
-
p.then(function(
|
|
12192
|
+
p.then(function(res2) {
|
|
11943
12193
|
try {
|
|
11944
|
-
var ct =
|
|
11945
|
-
if (/event-stream|octet|video|audio|image/i.test(ct || "")) return rec("fetch", method, url, hs, b,
|
|
11946
|
-
|
|
11947
|
-
rec("fetch", method, url, hs, b,
|
|
12194
|
+
var ct = res2.headers.get("content-type");
|
|
12195
|
+
if (/event-stream|octet|video|audio|image/i.test(ct || "")) return rec("fetch", method, url, hs, b, res2.status, ct, void 0, at);
|
|
12196
|
+
res2.clone().text().then(function(t) {
|
|
12197
|
+
rec("fetch", method, url, hs, b, res2.status, ct, cap(t), at);
|
|
11948
12198
|
}, function() {
|
|
11949
|
-
rec("fetch", method, url, hs, b,
|
|
12199
|
+
rec("fetch", method, url, hs, b, res2.status, ct, void 0, at);
|
|
11950
12200
|
});
|
|
11951
12201
|
} catch (e) {
|
|
11952
12202
|
}
|
|
@@ -11971,7 +12221,7 @@ function pageKit(opts) {
|
|
|
11971
12221
|
patch(X, "setRequestHeader", function(oSet) {
|
|
11972
12222
|
return function(k, v) {
|
|
11973
12223
|
try {
|
|
11974
|
-
if (this.__ifkI) this.__ifkI.h[k] = AUTH_H.test(k) ? v : 1;
|
|
12224
|
+
if (this.__ifkI) this.__ifkI.h[k] = AUTH_H.test(k) || /^next-action$/i.test(k) ? v : 1;
|
|
11975
12225
|
} catch (e) {
|
|
11976
12226
|
}
|
|
11977
12227
|
return oSet.apply(this, arguments);
|
|
@@ -12107,10 +12357,43 @@ function pageKit(opts) {
|
|
|
12107
12357
|
} catch (e) {
|
|
12108
12358
|
}
|
|
12109
12359
|
w.addEventListener("pagehide", persist);
|
|
12360
|
+
function retpl(s) {
|
|
12361
|
+
var x = /^(https?:\/\/[^\/]+)(\/.*)?$/.exec(s);
|
|
12362
|
+
return x ? x[1] + tpl(x[2] || "/") : s;
|
|
12363
|
+
}
|
|
12364
|
+
function rekey() {
|
|
12365
|
+
scanNext();
|
|
12366
|
+
var m2 = {};
|
|
12367
|
+
Object.keys(m).forEach(function(k) {
|
|
12368
|
+
var e = m[k], sp = k.indexOf(" "), rest = k.slice(sp + 1).split("#")[0];
|
|
12369
|
+
var nk = k.slice(0, sp) + " " + retpl(rest) + (e.next ? "#" + actionLabel(e.next.action) : "");
|
|
12370
|
+
if (e.next && acts[e.next.action]) e.next.name = acts[e.next.action];
|
|
12371
|
+
var p = m2[nk];
|
|
12372
|
+
if (!p) {
|
|
12373
|
+
m2[nk] = e;
|
|
12374
|
+
return;
|
|
12375
|
+
}
|
|
12376
|
+
p.n += e.n;
|
|
12377
|
+
["status", "query", "headers", "auth"].forEach(function(f) {
|
|
12378
|
+
(e[f] || []).forEach(function(x) {
|
|
12379
|
+
if (p[f].indexOf(x) < 0) p[f].push(x);
|
|
12380
|
+
});
|
|
12381
|
+
});
|
|
12382
|
+
if (!p.req) p.req = e.req;
|
|
12383
|
+
if (!p.res) p.res = e.res;
|
|
12384
|
+
});
|
|
12385
|
+
m = m2;
|
|
12386
|
+
var po2 = {};
|
|
12387
|
+
Object.keys(po).forEach(function(k) {
|
|
12388
|
+
po2[retpl(k)] = po[k];
|
|
12389
|
+
});
|
|
12390
|
+
po = po2;
|
|
12391
|
+
}
|
|
12110
12392
|
function report() {
|
|
12393
|
+
rekey();
|
|
12111
12394
|
var hooked = {};
|
|
12112
12395
|
Object.keys(m).forEach(function(k) {
|
|
12113
|
-
hooked[k.slice(k.indexOf(" ") + 1)] = 1;
|
|
12396
|
+
hooked[k.slice(k.indexOf(" ") + 1).split("#")[0]] = 1;
|
|
12114
12397
|
});
|
|
12115
12398
|
var pathOnly = Object.keys(po).filter(function(p) {
|
|
12116
12399
|
return !hooked[p];
|
|
@@ -12145,6 +12428,7 @@ function pageKit(opts) {
|
|
|
12145
12428
|
}
|
|
12146
12429
|
w.__ifk = {
|
|
12147
12430
|
version: VERSION,
|
|
12431
|
+
build: BUILD,
|
|
12148
12432
|
mode,
|
|
12149
12433
|
summary: function() {
|
|
12150
12434
|
var c = report().coverage;
|
|
@@ -12189,6 +12473,9 @@ function pageKit(opts) {
|
|
|
12189
12473
|
}
|
|
12190
12474
|
budget.calls++;
|
|
12191
12475
|
var init = { method, credentials: "include", headers: o.headers || {} };
|
|
12476
|
+
if ((o.rsc || /[?&]_rsc=/.test(String(path15))) && !Object.keys(init.headers).some(function(k) {
|
|
12477
|
+
return k.toLowerCase() === "rsc";
|
|
12478
|
+
})) init.headers.rsc = "1";
|
|
12192
12479
|
var used = [];
|
|
12193
12480
|
if (u.origin === location.origin && o.auth !== false) {
|
|
12194
12481
|
var a = readAuth();
|
|
@@ -12223,7 +12510,20 @@ function pageKit(opts) {
|
|
|
12223
12510
|
out.stopped = wall;
|
|
12224
12511
|
}
|
|
12225
12512
|
persist();
|
|
12226
|
-
|
|
12513
|
+
var ct = r.headers.get("content-type");
|
|
12514
|
+
if (isFlight(ct)) {
|
|
12515
|
+
learnParams(t);
|
|
12516
|
+
var data = flightData(parseFlight(t));
|
|
12517
|
+
if (data.action !== void 0) data = data.action;
|
|
12518
|
+
var size = JSON.stringify(data === void 0 ? null : data).length;
|
|
12519
|
+
if (size > maxBytes && !o.full) {
|
|
12520
|
+
out.shape = shape(data);
|
|
12521
|
+
out.bytes = size;
|
|
12522
|
+
out.hint = "big result, shown as its shape. Pass {full:true} and pick fields in the same evaluate: .then(r => r.data...)";
|
|
12523
|
+
} else out.data = data;
|
|
12524
|
+
return out;
|
|
12525
|
+
}
|
|
12526
|
+
if (t.length > maxBytes && !o.full) {
|
|
12227
12527
|
out.truncated = t.length;
|
|
12228
12528
|
t = t.slice(0, maxBytes);
|
|
12229
12529
|
}
|
|
@@ -12231,6 +12531,7 @@ function pageKit(opts) {
|
|
|
12231
12531
|
out.data = JSON.parse(t);
|
|
12232
12532
|
} catch (e) {
|
|
12233
12533
|
out.text = t.slice(0, 2e3);
|
|
12534
|
+
if (/html/i.test(ct || "")) out.hint = "got HTML, not data. On Next.js pages pass {rsc:true}";
|
|
12234
12535
|
}
|
|
12235
12536
|
return out;
|
|
12236
12537
|
});
|
|
@@ -12238,6 +12539,37 @@ function pageKit(opts) {
|
|
|
12238
12539
|
return { error: String(e && e.message || e) };
|
|
12239
12540
|
});
|
|
12240
12541
|
},
|
|
12542
|
+
// Calls a Next.js Server Action by export name (or id). The id is looked up
|
|
12543
|
+
// in the current page's chunks first, so it survives deploys.
|
|
12544
|
+
action: function(nameOrId, args2, o) {
|
|
12545
|
+
o = o || {};
|
|
12546
|
+
if (budget.stopped) return Promise.resolve({ error: "stopped", reason: budget.stopped });
|
|
12547
|
+
if (!o.approved) return Promise.resolve({ error: "write needs approval", hint: "a Server Action runs server code and may change data: ask the user, then pass {approved:true}" });
|
|
12548
|
+
var self = this;
|
|
12549
|
+
return scanActions(true).then(function(found) {
|
|
12550
|
+
var id = null;
|
|
12551
|
+
Object.keys(found).forEach(function(k) {
|
|
12552
|
+
if (found[k] === nameOrId || k === nameOrId) id = k;
|
|
12553
|
+
});
|
|
12554
|
+
if (!id) Object.keys(acts).forEach(function(k) {
|
|
12555
|
+
if (acts[k] === nameOrId) id = k;
|
|
12556
|
+
});
|
|
12557
|
+
if (!id && /^[0-9a-f]{40,44}$/.test(nameOrId)) id = nameOrId;
|
|
12558
|
+
if (!id) return { error: "action not found", hint: "open a page that uses " + nameOrId + ", re-run the install eval, then retry" };
|
|
12559
|
+
var body = JSON.stringify(Array.isArray(args2) ? args2 : args2 === void 0 ? [] : [args2]);
|
|
12560
|
+
return self.call("POST", o.path || location.pathname + location.search, {
|
|
12561
|
+
approved: true,
|
|
12562
|
+
full: o.full,
|
|
12563
|
+
maxBytes: o.maxBytes,
|
|
12564
|
+
headers: { "next-action": id, accept: "text/x-component", "content-type": "text/plain;charset=UTF-8" },
|
|
12565
|
+
body
|
|
12566
|
+
}).then(function(r) {
|
|
12567
|
+
if (r && r.status === 404) r.hint = "Server Action id not accepted (new deploy?): reload the page, re-run the install eval, retry";
|
|
12568
|
+
if (r) r.actionId = id.slice(0, 8);
|
|
12569
|
+
return r;
|
|
12570
|
+
});
|
|
12571
|
+
});
|
|
12572
|
+
},
|
|
12241
12573
|
// Removes every hook and forgets the capture.
|
|
12242
12574
|
stop: function() {
|
|
12243
12575
|
restores.forEach(function(r) {
|
|
@@ -12271,7 +12603,12 @@ function pageKit(opts) {
|
|
|
12271
12603
|
}
|
|
12272
12604
|
};
|
|
12273
12605
|
persist();
|
|
12274
|
-
|
|
12606
|
+
scanNext();
|
|
12607
|
+
if (w.__next_f || w.__NEXT_DATA__) scanActions(false);
|
|
12608
|
+
var res = { installed: true, again: false, version: VERSION, build: BUILD, mode, restored };
|
|
12609
|
+
if (replaced) res.replaced = replaced;
|
|
12610
|
+
if (w.__next_f || w.__NEXT_DATA__) res.nextjs = true;
|
|
12611
|
+
return res;
|
|
12275
12612
|
}
|
|
12276
12613
|
var init_page_kit = __esm({
|
|
12277
12614
|
"src/lib/kit/page-kit.ts"() {
|
|
@@ -12374,7 +12711,7 @@ function scanCapture(report) {
|
|
|
12374
12711
|
for (const [key, ep] of Object.entries(report.endpoints)) {
|
|
12375
12712
|
const where = `endpoints["${key.length > 80 ? key.slice(0, 80) + "\u2026" : key}"]`;
|
|
12376
12713
|
scanString(key, where, out);
|
|
12377
|
-
walkValues(ep, where, out);
|
|
12714
|
+
walkValues({ ...ep, next: void 0 }, where, out);
|
|
12378
12715
|
}
|
|
12379
12716
|
report.pathOnly.forEach((p, i) => scanString(p, `pathOnly[${i}]`, out));
|
|
12380
12717
|
return out;
|
|
@@ -12500,7 +12837,7 @@ function splitKey(key) {
|
|
|
12500
12837
|
const sp = key.indexOf(" ");
|
|
12501
12838
|
const method = key.slice(0, sp);
|
|
12502
12839
|
const rest = key.slice(sp + 1);
|
|
12503
|
-
const m = rest.match(/^(https?:\/\/[^/]+)(\/.*)?$/);
|
|
12840
|
+
const m = rest.split("#")[0].match(/^(https?:\/\/[^/]+)(\/.*)?$/);
|
|
12504
12841
|
return { method, origin: m ? m[1] : "", path: m?.[2] || "/" };
|
|
12505
12842
|
}
|
|
12506
12843
|
function pascal(s) {
|
|
@@ -12521,7 +12858,7 @@ function functionNameFor(method, p, res, kind) {
|
|
|
12521
12858
|
function buildApiMap(report) {
|
|
12522
12859
|
const used = /* @__PURE__ */ new Map();
|
|
12523
12860
|
const endpoints = Object.entries(report.endpoints).map(([key, ep]) => ({ key, ep, ...splitKey(key) })).sort((a, b) => a.path === b.path ? a.method.localeCompare(b.method) : a.path.localeCompare(b.path)).map(({ ep, method, origin, path: p }) => {
|
|
12524
|
-
let fn = functionNameFor(method, p, ep.res, ep.kind);
|
|
12861
|
+
let fn = ep.next?.name || (ep.next ? "action" + (pascal(p.split("/").filter((x) => x && !x.startsWith("{")).slice(-2).join(" ")) || "Root") : functionNameFor(method, p, ep.res, ep.kind));
|
|
12525
12862
|
const n = used.get(fn) ?? 0;
|
|
12526
12863
|
used.set(fn, n + 1);
|
|
12527
12864
|
if (n) fn += n + 1;
|
|
@@ -12538,7 +12875,9 @@ function buildApiMap(report) {
|
|
|
12538
12875
|
request: ep.req,
|
|
12539
12876
|
response: ep.res,
|
|
12540
12877
|
functionName: fn,
|
|
12541
|
-
...ep.redacted?.length ? { privateValues: ep.redacted } : {}
|
|
12878
|
+
...ep.redacted?.length ? { privateValues: ep.redacted } : {},
|
|
12879
|
+
...(ep.headers || []).includes("rsc") || (ep.query || []).includes("_rsc") ? { rsc: true } : {},
|
|
12880
|
+
...ep.next ? { action: { name: ep.next.name, id: ep.next.action } } : {}
|
|
12542
12881
|
};
|
|
12543
12882
|
});
|
|
12544
12883
|
return {
|
|
@@ -12560,6 +12899,17 @@ function describeAuthSource(s) {
|
|
|
12560
12899
|
const at = s.path?.length ? `${s.key} -> ${s.path.join(".")}` : s.key;
|
|
12561
12900
|
return `${s.from}["${at}"]${pre}; __ifk.call attaches it automatically`;
|
|
12562
12901
|
}
|
|
12902
|
+
function pathParams(p) {
|
|
12903
|
+
const used = /* @__PURE__ */ new Set();
|
|
12904
|
+
return (p.match(/\{([^}]+)\}/g) || []).map((m) => {
|
|
12905
|
+
let base = m.slice(1, -1).replace(/[^A-Za-z0-9_$]/g, "_");
|
|
12906
|
+
if (!/^[A-Za-z_$]/.test(base)) base = "p_" + base;
|
|
12907
|
+
let name = base, n = 2;
|
|
12908
|
+
while (used.has(name)) name = `${base}${n++}`;
|
|
12909
|
+
used.add(name);
|
|
12910
|
+
return name;
|
|
12911
|
+
});
|
|
12912
|
+
}
|
|
12563
12913
|
function takesBody(ep) {
|
|
12564
12914
|
return ep.request !== void 0 || ep.method === "POST" || ep.method === "PUT" || ep.method === "PATCH";
|
|
12565
12915
|
}
|
|
@@ -12575,6 +12925,10 @@ function renderClient(map) {
|
|
|
12575
12925
|
lines.push(` if (!window.__ifk) return Promise.resolve({ error: "NOT_INSTALLED: eval(sessionStorage.__ifk) first" });`);
|
|
12576
12926
|
lines.push(` return window.__ifk.call(m, p, o);`);
|
|
12577
12927
|
lines.push(` }`);
|
|
12928
|
+
lines.push(` function a(name, args, o) {`);
|
|
12929
|
+
lines.push(` if (!window.__ifk) return Promise.resolve({ error: "NOT_INSTALLED: eval(sessionStorage.__ifk) first" });`);
|
|
12930
|
+
lines.push(` return window.__ifk.action(name, args, o);`);
|
|
12931
|
+
lines.push(` }`);
|
|
12578
12932
|
lines.push(` function q(query) {`);
|
|
12579
12933
|
lines.push(` if (!query) return "";`);
|
|
12580
12934
|
lines.push(` var s = new URLSearchParams(query).toString();`);
|
|
@@ -12590,17 +12944,20 @@ function renderClient(map) {
|
|
|
12590
12944
|
skipped.push(`${ep.method} ${ep.origin}${ep.path} (cross-origin)`);
|
|
12591
12945
|
continue;
|
|
12592
12946
|
}
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
|
|
12947
|
+
if (ep.action) {
|
|
12948
|
+
const ref = JSON.stringify(ep.action.name || ep.action.id);
|
|
12949
|
+
lines.push(` // Next.js Server Action at ${ep.path}. args: array of the action's arguments. Needs {approved:true}.`);
|
|
12950
|
+
lines.push(` ${ep.functionName}: function (args, opts) { return a(${ref}, args, opts); },`);
|
|
12951
|
+
continue;
|
|
12952
|
+
}
|
|
12953
|
+
const params = pathParams(ep.path);
|
|
12954
|
+
let i = 0;
|
|
12955
|
+
const urlExpr = JSON.stringify(ep.path).replace(/\{[^}]+\}/g, () => `" + encodeURIComponent(${params[i++]}) + "`).replace(/ \+ ""$/, "").replace(/^"" \+ /, "");
|
|
12600
12956
|
const hasBody = takesBody(ep);
|
|
12601
12957
|
if (hasBody) params.push("body");
|
|
12602
12958
|
params.push("query", "opts");
|
|
12603
|
-
const
|
|
12959
|
+
const base = [hasBody ? "json: body" : "", ep.rsc ? "rsc: true" : ""].filter(Boolean).join(", ");
|
|
12960
|
+
const optsExpr = base ? `Object.assign({ ${base} }, opts)` : `opts`;
|
|
12604
12961
|
const doc = [
|
|
12605
12962
|
ep.query.length ? `query: ${ep.query.join(", ")}` : "",
|
|
12606
12963
|
ep.status.length ? `seen ${ep.status.join("/")}` : ""
|
|
@@ -12635,11 +12992,18 @@ ${indent}}`;
|
|
|
12635
12992
|
function renderTypes(map) {
|
|
12636
12993
|
const out = [`// Generated by \`iframer ingest\` from ${map.origin}. Shapes come from one sample each: fields may be optional.`];
|
|
12637
12994
|
out.push(`type IfkResult<T> = Promise<{ status?: number; data?: T; text?: string; truncated?: number; error?: string; stopped?: string }>;`);
|
|
12638
|
-
out.push(`type IfkOpts = { approved?: boolean; headers?: Record<string, string>; maxBytes?: number };`);
|
|
12995
|
+
out.push(`type IfkOpts = { approved?: boolean; headers?: Record<string, string>; maxBytes?: number; full?: boolean; rsc?: boolean; auth?: boolean };`);
|
|
12639
12996
|
out.push(`interface IfkApi {`);
|
|
12640
12997
|
for (const ep of map.endpoints) {
|
|
12641
12998
|
if (ep.kind === "ws" || ep.kind === "sse" || ep.kind === "beacon" || ep.origin !== map.origin) continue;
|
|
12642
|
-
|
|
12999
|
+
if (ep.action) {
|
|
13000
|
+
const args2 = ep.request?.json !== void 0 ? tsType(ep.request.json, " ") : "unknown[]";
|
|
13001
|
+
const r = ep.response?.rsc?.action;
|
|
13002
|
+
out.push(` /** Next.js Server Action ${ep.action.name || ep.action.id.slice(0, 8)} at ${ep.path} */`);
|
|
13003
|
+
out.push(` ${ep.functionName}(args: ${args2}, opts?: IfkOpts): IfkResult<${r !== void 0 ? tsType(r, " ") : "unknown"}>;`);
|
|
13004
|
+
continue;
|
|
13005
|
+
}
|
|
13006
|
+
const ids = pathParams(ep.path).map((n) => `${n}: string | number`);
|
|
12643
13007
|
const hasBody = takesBody(ep);
|
|
12644
13008
|
const req = ep.request?.json !== void 0 ? tsType(ep.request.json, " ") : ep.request?.form || ep.request?.multipart ? "Record<string, string>" : "unknown";
|
|
12645
13009
|
const res = ep.response && typeof ep.response === "object" && !Array.isArray(ep.response) && "contentType" in ep.response && "bytes" in ep.response ? "unknown" : tsType(ep.response, " ");
|
|
@@ -12663,7 +13027,7 @@ function shortShape(s, depth = 0) {
|
|
|
12663
13027
|
return "?";
|
|
12664
13028
|
}
|
|
12665
13029
|
function describeEndpoint(ep) {
|
|
12666
|
-
const parts = [ep.kind, ep.status.length ? `status ${ep.status.join("/")}` : ""];
|
|
13030
|
+
const parts = [ep.action ? `Next.js Server Action ${ep.action.name || ep.action.id.slice(0, 8)}` : ep.kind, ep.rsc ? "RSC (page data)" : "", ep.status.length ? `status ${ep.status.join("/")}` : ""];
|
|
12667
13031
|
if (ep.request?.json !== void 0) parts.push(`body ${shortShape(ep.request.json)}`);
|
|
12668
13032
|
else if (ep.request?.form) parts.push(`form ${Object.keys(ep.request.form).join(",")}`);
|
|
12669
13033
|
if (ep.response !== void 0) parts.push(`returns ${shortShape(ep.response)}`);
|
|
@@ -12719,7 +13083,9 @@ function kitSource(opts = {}) {
|
|
|
12719
13083
|
if (opts.hostFilter) clean.hostFilter = opts.hostFilter;
|
|
12720
13084
|
if (opts.maxBody) clean.maxBody = opts.maxBody;
|
|
12721
13085
|
if (opts.maxCalls) clean.maxCalls = opts.maxCalls;
|
|
12722
|
-
|
|
13086
|
+
const fn = pageKit.toString();
|
|
13087
|
+
const build = import_crypto13.default.createHash("sha1").update(fn).digest("hex").slice(0, 10);
|
|
13088
|
+
return `(${fn})(${JSON.stringify({ ...clean, build })});
|
|
12723
13089
|
`;
|
|
12724
13090
|
}
|
|
12725
13091
|
function kitDir() {
|
|
@@ -12736,6 +13102,7 @@ function kitInstructions(target, kitPath, clientPath) {
|
|
|
12736
13102
|
`Rules:`,
|
|
12737
13103
|
`- After EVERY full page load, run \`${INSTALL}\` again. It restores what was captured. Check with \`${STATUS}\`.`,
|
|
12738
13104
|
`- Drive the site with your normal tools while it records. Only shapes are kept, never values.`,
|
|
13105
|
+
`- Next.js sites: page data comes back from \`window.__ifk.call('GET','/path',{rsc:true})\` as {components:{Name:props}}. Server Actions: \`window.__ifk.action('exportName',[args],{approved:true})\` (the id is looked up again after deploys).`,
|
|
12739
13106
|
`- Replay: \`window.__ifk.call('GET','/path')\` runs in the page with the user's cookies. Writes need {approved:true}, so ask the user first. It stops on 403, 429 or a captcha page: do not retry.`,
|
|
12740
13107
|
`- Coverage is never complete: workers, redirect hops, HttpOnly cookies and cross-origin iframes are invisible to the kit.`,
|
|
12741
13108
|
`- Done: \`window.__ifk.stop()\` removes the hooks.`
|
|
@@ -12804,10 +13171,11 @@ function drainFallback() {
|
|
|
12804
13171
|
`until next is null, write the joined chunks to a file, then \`iframer ingest <file>\`.`
|
|
12805
13172
|
].join("\n");
|
|
12806
13173
|
}
|
|
12807
|
-
var import_fs12, import_path13, KIT_TARGETS, MAKE_INPUT, SS, stash, STASH, INSTALL, INSTALL_CLIENT, STATUS;
|
|
13174
|
+
var import_crypto13, import_fs12, import_path13, KIT_TARGETS, MAKE_INPUT, SS, stash, STASH, INSTALL, INSTALL_CLIENT, STATUS;
|
|
12808
13175
|
var init_kit = __esm({
|
|
12809
13176
|
"src/lib/kit/index.ts"() {
|
|
12810
13177
|
"use strict";
|
|
13178
|
+
import_crypto13 = __toESM(require("crypto"));
|
|
12811
13179
|
import_fs12 = __toESM(require("fs"));
|
|
12812
13180
|
import_path13 = __toESM(require("path"));
|
|
12813
13181
|
init_paths();
|
|
@@ -13914,6 +14282,14 @@ Screenshot: ${shot}`);
|
|
|
13914
14282
|
break;
|
|
13915
14283
|
}
|
|
13916
14284
|
// ─── In-page capture kit (any agent browser, T3 Code first) ─────
|
|
14285
|
+
case "refresh": {
|
|
14286
|
+
const kit = await Promise.resolve().then(() => (init_kit(), kit_exports));
|
|
14287
|
+
const kitPath = kit.writeKit({});
|
|
14288
|
+
const skill = installSkill();
|
|
14289
|
+
console.log(` Kit: ${kitPath}`);
|
|
14290
|
+
console.log(skill ? " Skill: ~/.claude/commands/iframer.md" : " Skill: not found in the package, skipped");
|
|
14291
|
+
break;
|
|
14292
|
+
}
|
|
13917
14293
|
case "kit": {
|
|
13918
14294
|
const kit = await Promise.resolve().then(() => (init_kit(), kit_exports));
|
|
13919
14295
|
const target = parseFlag(args, "--for") || "t3";
|
|
@@ -14266,6 +14642,11 @@ Screenshot: ${shot}`);
|
|
|
14266
14642
|
console.error(" sudo npm install -g iframer-toolkit@latest");
|
|
14267
14643
|
process.exit(1);
|
|
14268
14644
|
}
|
|
14645
|
+
try {
|
|
14646
|
+
require("child_process").execSync("iframer refresh", { stdio: "inherit" });
|
|
14647
|
+
} catch {
|
|
14648
|
+
console.error(" Could not refresh the kit and skill. Run: iframer refresh");
|
|
14649
|
+
}
|
|
14269
14650
|
console.log();
|
|
14270
14651
|
const hasExt = extensionInstalled();
|
|
14271
14652
|
await reloadAndRestartServer(hasExt);
|
|
@@ -14389,6 +14770,7 @@ Screenshot: ${shot}`);
|
|
|
14389
14770
|
extension path Print the extension folder to load in chrome://extensions
|
|
14390
14771
|
extension token Print the pairing token (only needed to pair by hand)
|
|
14391
14772
|
update Update iframer via npm; reload the extension too if it's installed
|
|
14773
|
+
refresh Rewrite the in-page kit and the /iframer skill from this version (update does it)
|
|
14392
14774
|
update --check Report whether a newer version is available (no install)
|
|
14393
14775
|
version, --version, -v Print the installed version
|
|
14394
14776
|
remove Remove everything (Chromium + MCP)
|
package/extension/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "iframer",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.2",
|
|
5
5
|
"description": "Let iframer drive a Chrome tab you choose, on your real logged-in session.",
|
|
6
6
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp2wbce98k+xDnEgtuzLyNXYBLEDY0nqChuvolb94B3VV1HsHFUulUyNe92eN6MjQuadDohQ2j5mj7zWs4yQoixgTQPe7Gm/+GHWdgxwM2s9t5opnLHwF90LPiJm6q1NGWLM4aK6JT+2I6FnUGmj2bzB4WIlf55C3aMEBoXTqIzG2U8s2cxkXGebWWqbPlMK/Phsmsvo4POWnidbGHPhBOq0iyPTu7RdSDlNdJdJ+dtdNXkMmltKUS881MQI9T6GcCCKhIKcZrJGGnk0xphlWyl+ublTAO0tbQSVB/K7D3JmCxMv690TPtYUPPhGi+C6VCv3IeutLmNo60mvux3wbGQIDAQAB",
|
|
7
7
|
"permissions": [
|
package/package.json
CHANGED
package/skills/iframer.md
CHANGED
|
@@ -182,6 +182,8 @@ When you already drive a browser through your own tools (a T3 Code preview tab,
|
|
|
182
182
|
- Writes need `{approved:true}`: ask the user first. Replays stop for good on 403, 429 or a captcha page. Do not retry or work around it.
|
|
183
183
|
- Sites that detect tampering: `iframer kit --mode passive` (no patching; URLs and form posts only).
|
|
184
184
|
- Report coverage as the kit states it. It is never complete: workers, redirect hops and cross-origin iframes are invisible.
|
|
185
|
+
- Next.js sites: route segments come out named (`/{communitySlug}/...`) from the app's own route data. Page data: `window.__ifk.call('GET', path, {rsc:true})` (the generated client sets it) returns `{components: {Name: props}}`, not HTML. Server Actions: `window.__ifk.action('exportName', [args], {approved:true})`, or the client function of the same name. If a result is big you get its shape: pass `{full:true}` and pick fields in the same evaluate.
|
|
186
|
+
- After `iframer update`, the kit file is rewritten. Installing it in a tab that still runs an older kit replaces it and keeps the capture.
|
|
185
187
|
- Private values are separated from the API, not a reason to stop. `ingest` swaps anything token-, email- or phone-like for a placeholder (`{id}` in paths, `{key}` for keys) and lists what it replaced. Endpoints that had one carry `privateValues` in api.json: pass that value per call.
|
|
186
188
|
- Identity: api.json `identity` (and the knowledge file) says which auth headers the site sends and where the page keeps each value, e.g. `localStorage["session -> user.token"]`. `__ifk.call` attaches them on its own, cookies included. If a call returns 401 with a `hint`, use the site once so it sends its token, then retry.
|
|
187
189
|
|