@surething/cockpit 1.0.233 → 1.0.234
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/.next-prod/BUILD_ID +1 -1
- package/.next-prod/app-path-routes-manifest.json +2 -2
- package/.next-prod/build-manifest.json +3 -3
- package/.next-prod/prerender-manifest.json +3 -3
- package/.next-prod/react-loadable-manifest.json +7 -0
- package/.next-prod/server/app/_global-error/page.js.nft.json +1 -1
- package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_global-error.html +1 -1
- package/.next-prod/server/app/_global-error.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found/page.js.nft.json +1 -1
- package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_not-found.html +2 -2
- package/.next-prod/server/app/_not-found.rsc +2 -2
- package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next-prod/server/app/api/agent/test/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/bash/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/codex/route.js +1 -1
- package/.next-prod/server/app/api/chat/codex/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/deepseek/route.js +1 -1
- package/.next-prod/server/app/api/chat/deepseek/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/kimi/route.js +1 -1
- package/.next-prod/server/app/api/chat/kimi/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/ollama/route.js +1 -1
- package/.next-prod/server/app/api/chat/ollama/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/pty-input/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/route.js +1 -1
- package/.next-prod/server/app/api/chat/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/stop/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/claude-stats/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/commands/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/comments/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/columns/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/export/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/schemas/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/dev/spans/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/extension/version/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/file/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/blame/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/clipboard/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/copy/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/delete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/expanded/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/index/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/init/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/paste/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/read/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/readdir/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/recent/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/save/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/search/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/stat/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/text/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/branch-diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/branches/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/commit-diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/commits/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/current-branch/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/discard/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/stage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/status/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/unstage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/worktree/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/global-state/route.js +1 -1
- package/.next-prod/server/app/api/global-state/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/health/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/load/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/save/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/shutdown/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/definition/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/hover/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/references/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/status/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/warmup/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/columns/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/export/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/schemas/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/schema/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/note/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/ollama/models/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/ollama/start/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/open-cursor/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/open-vscode/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/pick-folder/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/pinned-sessions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/project-settings/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/project-state/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/affected/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/callees/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/callers/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/coedit/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/context/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/file/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/file-functions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/impact/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/related/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/risk/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/search/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projects/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/push/public-key/route.js +1 -1
- package/.next-prod/server/app/api/push/public-key/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/push/subscribe/route.js +1 -1
- package/.next-prod/server/app/api/push/subscribe/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/push/unsubscribe/route.js +1 -1
- package/.next-prod/server/app/api/push/unsubscribe/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/command/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/delete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/get/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/keys/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/set/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/comments/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/replies/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/identify/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/order/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/share-info/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/users/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/scheduled-tasks/route.js +1 -1
- package/.next-prod/server/app/api/scheduled-tasks/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/services/config/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/services/scripts/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session/[sessionId]/fork/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session/[sessionId]/history/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session-by-path/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/projects/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/settings/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/[id]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/content/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/aliases/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/autocomplete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/bubble-order/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/env/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/history/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/version/route.js.nft.json +1 -1
- package/.next-prod/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next-prod/server/app/m/page.js +2 -2
- package/.next-prod/server/app/m/page.js.nft.json +1 -1
- package/.next-prod/server/app/m/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/manifest.webmanifest/route.js.nft.json +1 -1
- package/.next-prod/server/app/page.js +1 -1
- package/.next-prod/server/app/page.js.nft.json +1 -1
- package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/project/page.js +1 -1
- package/.next-prod/server/app/project/page.js.nft.json +1 -1
- package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/review/[id]/page.js +2 -2
- package/.next-prod/server/app/review/[id]/page.js.nft.json +1 -1
- package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app-paths-manifest.json +2 -2
- package/.next-prod/server/chunks/{1035.js → 5897.js} +2 -2
- package/.next-prod/server/chunks/9658.js +26 -26
- package/.next-prod/server/chunks/9830.js +1 -1
- package/.next-prod/server/middleware-build-manifest.js +1 -1
- package/.next-prod/server/middleware-react-loadable-manifest.js +1 -1
- package/.next-prod/server/pages/404.html +2 -2
- package/.next-prod/server/pages/500.html +1 -1
- package/.next-prod/server/server-reference-manifest.json +1 -1
- package/.next-prod/static/chunks/{2625-bc4748f0cd7db618.js → 1332-5e141e2acf28015a.js} +10 -10
- package/.next-prod/static/chunks/{217-92f800c2c438c550.js → 217-e929c46c0bd599a7.js} +2 -2
- package/.next-prod/static/chunks/2502-fb8682bc7a61e04d.js +4 -0
- package/.next-prod/static/chunks/4523.3f277a24fbcfde6d.js +138 -0
- package/.next-prod/static/chunks/9304-ddde9008da31b2ce.js +1 -0
- package/.next-prod/static/chunks/app/{layout-ac2f1f67df01cc89.js → layout-d202f8e0bcc90661.js} +1 -1
- package/.next-prod/static/chunks/app/m/page-71570a066f3eacf8.js +1 -0
- package/.next-prod/static/chunks/app/{page-1f0a6f49656a4c52.js → page-814b50b5b440249d.js} +1 -1
- package/.next-prod/static/chunks/app/project/{page-1f0a6f49656a4c52.js → page-814b50b5b440249d.js} +1 -1
- package/.next-prod/static/chunks/app/review/[id]/{page-39fa3a843adf617e.js → page-0c5d03d65758c7dc.js} +1 -1
- package/.next-prod/static/chunks/webpack-2c71a79c277961bc.js +1 -0
- package/.next-prod/trace +13 -13
- package/.next-prod/trace-build +1 -1
- package/bin/cock-browser.mjs +12 -9
- package/bin/cock.mjs +5 -5
- package/bin/postinstall.mjs +6 -6
- package/chrome-extension/automation.js +45 -40
- package/chrome-extension/background.js +70 -67
- package/chrome-extension/content.js +38 -38
- package/chrome-extension/disguise.js +4 -4
- package/chrome-extension/network-capture.js +16 -16
- package/dist/{chunk-VHFWPI2X.mjs → chunk-3M3JSZ5R.mjs} +28 -1
- package/dist/scheduledTasks.mjs +1 -1
- package/dist/wsServer.mjs +6 -31
- package/package.json +1 -1
- package/server.mjs +122 -26
- package/.next-prod/server/chunks/7003.js +0 -1
- package/.next-prod/static/chunks/471-504b396c8302f4f2.js +0 -4
- package/.next-prod/static/chunks/app/m/page-8b1af6f31b60c485.js +0 -1
- package/.next-prod/static/chunks/webpack-dbba7fa77ee1faa4.js +0 -1
- /package/.next-prod/static/{WfNQMmytl62j_8KtgCW9_ → JfwcuC5YjyhQWeQD-4Gvn}/_buildManifest.js +0 -0
- /package/.next-prod/static/{WfNQMmytl62j_8KtgCW9_ → JfwcuC5YjyhQWeQD-4Gvn}/_ssgManifest.js +0 -0
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cockpit Bridge - Background Service Worker
|
|
3
3
|
*
|
|
4
|
-
* iframe Cookie
|
|
5
|
-
*
|
|
4
|
+
* iframe Cookie injection: use declarativeNetRequest dynamic rules to inject the Cookie header at the network layer
|
|
5
|
+
* Does not modify the global cookie store; only adds the Cookie at the request level
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// =========================================================================
|
|
9
|
-
// iframe Cookie
|
|
9
|
+
// iframe Cookie injection
|
|
10
10
|
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
11
|
+
// Idea: cross-site requests from an iframe do not carry SameSite=Lax cookies.
|
|
12
|
+
// Use chrome.cookies.getAll to read all cookies for the target domain,
|
|
13
|
+
// then set the Cookie request header at the network layer via declarativeNetRequest dynamic rules.
|
|
14
|
+
// The cookie store is not modified; only request headers are affected.
|
|
15
15
|
//
|
|
16
|
-
//
|
|
17
|
-
// resourceTypes
|
|
16
|
+
// The dynamic rules use requestDomains to scope the domain, tabIds to scope the Cockpit tab,
|
|
17
|
+
// and resourceTypes to cover sub_frame + sub-resources (XHR/script/css/image, etc.).
|
|
18
18
|
// =========================================================================
|
|
19
19
|
|
|
20
|
-
//
|
|
20
|
+
// On startup, clean up session rules left over from last time (after the extension reloads, the in-memory map is lost but the rules are still in effect)
|
|
21
21
|
chrome.declarativeNetRequest.getSessionRules().then(rules => {
|
|
22
22
|
if (rules.length) {
|
|
23
23
|
const ids = rules.map(r => r.id);
|
|
@@ -27,23 +27,23 @@ chrome.declarativeNetRequest.getSessionRules().then(rules => {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
// =========================================================================
|
|
30
|
-
// Cockpit iframe
|
|
30
|
+
// Cockpit iframe tracking + cookie injection ordering guarantee
|
|
31
31
|
//
|
|
32
|
-
//
|
|
33
|
-
// Layer 1: externally_connectable
|
|
34
|
-
// → await prepareCookies()
|
|
35
|
-
// →
|
|
32
|
+
// Two-layer guarantee mechanism:
|
|
33
|
+
// Layer 1: externally_connectable (BrowserBubble → background direct connection)
|
|
34
|
+
// → the iframe src is only set after `await prepareCookies()` returns
|
|
35
|
+
// → cookie rules are 100% ready before the first request
|
|
36
36
|
//
|
|
37
|
-
// Layer 2: webNavigation.onBeforeNavigate
|
|
38
|
-
// →
|
|
39
|
-
// →
|
|
37
|
+
// Layer 2: webNavigation.onBeforeNavigate (fallback + frame tracking)
|
|
38
|
+
// → records frames carrying _cockpit=1, for content script check-frame queries
|
|
39
|
+
// → also triggers injectCookiesForUrl as a fallback (refresh scenarios, etc.)
|
|
40
40
|
//
|
|
41
|
-
// DNR
|
|
41
|
+
// DNR static rule #3 strips the _cockpit=1 parameter at the network layer, so the server never sees it.
|
|
42
42
|
// =========================================================================
|
|
43
43
|
const cockpitFrames = new Set(); // "tabId-frameId"
|
|
44
44
|
|
|
45
45
|
chrome.webNavigation.onBeforeNavigate.addListener((details) => {
|
|
46
|
-
//
|
|
46
|
+
// Only care about iframes (frameId > 0)
|
|
47
47
|
if (details.frameId === 0) return;
|
|
48
48
|
|
|
49
49
|
const key = `${details.tabId}-${details.frameId}`;
|
|
@@ -51,8 +51,8 @@ chrome.webNavigation.onBeforeNavigate.addListener((details) => {
|
|
|
51
51
|
if (details.url.includes('_cockpit=1')) {
|
|
52
52
|
cockpitFrames.add(key);
|
|
53
53
|
|
|
54
|
-
//
|
|
55
|
-
//
|
|
54
|
+
// Fallback injection: only triggers when externally_connectable has not pre-created the rules
|
|
55
|
+
// Avoids duplicate injectCookiesForUrl calls causing a gap of "delete old rules first → asynchronously create new rules"
|
|
56
56
|
try {
|
|
57
57
|
const domain = new URL(details.url).hostname;
|
|
58
58
|
const ruleKey = `${domain}:${details.tabId || 'all'}`;
|
|
@@ -68,7 +68,7 @@ chrome.webNavigation.onBeforeNavigate.addListener((details) => {
|
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
// tab
|
|
71
|
+
// When a tab is closed, clean up all records for that tab
|
|
72
72
|
chrome.tabs.onRemoved.addListener((tabId) => {
|
|
73
73
|
for (const key of cockpitFrames) {
|
|
74
74
|
if (key.startsWith(`${tabId}-`)) {
|
|
@@ -77,7 +77,7 @@ chrome.tabs.onRemoved.addListener((tabId) => {
|
|
|
77
77
|
}
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
-
//
|
|
80
|
+
// Map of domains that already have injected cookies → list of rule IDs
|
|
81
81
|
let nextRuleId = 1000;
|
|
82
82
|
const domainRuleMap = new Map(); // "domain:tabId" → [ruleId, ruleId]
|
|
83
83
|
|
|
@@ -88,7 +88,7 @@ async function injectCookiesForUrl(url, tabId) {
|
|
|
88
88
|
const domain = urlObj.hostname;
|
|
89
89
|
const key = `${domain}:${tabId || 'all'}`;
|
|
90
90
|
|
|
91
|
-
//
|
|
91
|
+
// If rules already exist, clean them up first
|
|
92
92
|
if (domainRuleMap.has(key)) {
|
|
93
93
|
const oldIds = domainRuleMap.get(key);
|
|
94
94
|
await chrome.declarativeNetRequest.updateSessionRules({
|
|
@@ -96,24 +96,24 @@ async function injectCookiesForUrl(url, tabId) {
|
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
// domain=api.github.com (
|
|
102
|
-
// domain=.github.com (
|
|
103
|
-
// domain=.com
|
|
99
|
+
// Read cookies for this domain and all of its parent domains
|
|
100
|
+
// e.g. api.github.com needs to collect:
|
|
101
|
+
// domain=api.github.com (exact match)
|
|
102
|
+
// domain=.github.com (parent domain, obtained via getAll({ domain: 'github.com' }))
|
|
103
|
+
// domain=.com is not needed (public suffix, and it cannot have cookies anyway)
|
|
104
104
|
const domainParts = domain.split('.');
|
|
105
105
|
const domainsToQuery = [];
|
|
106
106
|
for (let i = 0; i < domainParts.length - 1; i++) {
|
|
107
107
|
domainsToQuery.push(domainParts.slice(i).join('.'));
|
|
108
108
|
}
|
|
109
|
-
//
|
|
110
|
-
const cookieMap = new Map(); // name+domain+path → cookie
|
|
109
|
+
// Collect all cookies with deduplication
|
|
110
|
+
const cookieMap = new Map(); // name+domain+path → cookie (deduplicated)
|
|
111
111
|
for (const d of domainsToQuery) {
|
|
112
112
|
const result = await chrome.cookies.getAll({ domain: d });
|
|
113
113
|
for (const c of result) {
|
|
114
|
-
//
|
|
115
|
-
// .github.com
|
|
116
|
-
// .example.com
|
|
114
|
+
// Verify the cookie's domain actually matches the current domain
|
|
115
|
+
// .github.com matches api.github.com ✓
|
|
116
|
+
// .example.com does not match api.github.com ✗
|
|
117
117
|
const cookieDomain = c.domain.startsWith('.') ? c.domain.slice(1) : c.domain;
|
|
118
118
|
if (domain === cookieDomain || domain.endsWith('.' + cookieDomain)) {
|
|
119
119
|
cookieMap.set(`${c.name}|${c.domain}|${c.path}`, c);
|
|
@@ -126,9 +126,9 @@ async function injectCookiesForUrl(url, tabId) {
|
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
//
|
|
129
|
+
// Filter for cookies blocked by the browser (Lax/Strict/unset)
|
|
130
|
+
// The browser only auto-sends SameSite=None cookies; the rest we need to append ourselves
|
|
131
|
+
// Using append does not override the None versions the browser sends; same-name cookies coexisting is equivalent to a normal top-level visit
|
|
132
132
|
const blockedCookies = cookies.filter(c => c.sameSite !== 'none');
|
|
133
133
|
|
|
134
134
|
if (!blockedCookies.length) {
|
|
@@ -136,9 +136,9 @@ async function injectCookiesForUrl(url, tabId) {
|
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
//
|
|
140
|
-
// .google.com
|
|
141
|
-
// console.cloud.google.com
|
|
139
|
+
// Group cookies by their effective domain
|
|
140
|
+
// Cookies for .google.com → requestDomains: ['google.com'] (covers accounts.google.com and all other subdomains)
|
|
141
|
+
// Cookies for console.cloud.google.com → requestDomains: ['console.cloud.google.com']
|
|
142
142
|
const domainGroups = new Map(); // effectiveDomain → [cookie, ...]
|
|
143
143
|
for (const c of blockedCookies) {
|
|
144
144
|
const effectiveDomain = c.domain.startsWith('.') ? c.domain.slice(1) : c.domain;
|
|
@@ -166,14 +166,14 @@ async function injectCookiesForUrl(url, tabId) {
|
|
|
166
166
|
],
|
|
167
167
|
};
|
|
168
168
|
|
|
169
|
-
//
|
|
169
|
+
// Rule A: sub_frame (iframe document load)
|
|
170
170
|
const ruleIdA = nextRuleId++;
|
|
171
171
|
const conditionA = {
|
|
172
172
|
requestDomains: [effectiveDomain],
|
|
173
173
|
resourceTypes: ['sub_frame'],
|
|
174
174
|
};
|
|
175
175
|
|
|
176
|
-
//
|
|
176
|
+
// Rule B: sub-resources (XHR/script/css etc., only those initiated inside the iframe)
|
|
177
177
|
const ruleIdB = nextRuleId++;
|
|
178
178
|
const conditionB = {
|
|
179
179
|
requestDomains: [effectiveDomain],
|
|
@@ -212,7 +212,7 @@ async function injectCookiesForUrl(url, tabId) {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
//
|
|
215
|
+
// Listen for messages
|
|
216
216
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
217
217
|
if (message.type === 'cockpit:reload') {
|
|
218
218
|
chrome.runtime.reload();
|
|
@@ -225,7 +225,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
225
225
|
return true;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
// content script
|
|
228
|
+
// content script asks whether the current frame is a Cockpit iframe (handles redirect scenarios)
|
|
229
229
|
if (message.type === 'cockpit:check-frame') {
|
|
230
230
|
const key = `${sender.tab?.id}-${sender.frameId}`;
|
|
231
231
|
const isCockpit = cockpitFrames.has(key);
|
|
@@ -234,8 +234,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
234
234
|
return;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
// evaluate
|
|
238
|
-
// allFrames: true →
|
|
237
|
+
// evaluate: run JS in the main world (bypasses the page's CSP restrictions)
|
|
238
|
+
// allFrames: true → execute in all frames (solves the cross-origin iframe access problem)
|
|
239
239
|
if (message.type === 'cockpit:evaluate') {
|
|
240
240
|
const tabId = sender.tab?.id;
|
|
241
241
|
const frameId = sender.frameId ?? 0;
|
|
@@ -250,13 +250,15 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
250
250
|
target,
|
|
251
251
|
world: 'MAIN',
|
|
252
252
|
func: async (code) => {
|
|
253
|
-
// Chrome extension messaging
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
253
|
+
// Chrome extension messaging has an implicit ~8 KiB truncation point at
|
|
254
|
+
// sendResponse's structured clone boundary (reproduced in session 6910d071
|
|
255
|
+
// where evaluate output stopped exactly at 8192 bytes). To avoid large
|
|
256
|
+
// results being silently truncated, we evaluate the result in the MAIN
|
|
257
|
+
// world first; if the serialized form is > CHUNK_THRESHOLD, we stash the
|
|
258
|
+
// full payload in a Map on the page's window and only return a descriptor
|
|
259
|
+
// to the upper layer. The cock-browser CLI recognizes this descriptor and
|
|
260
|
+
// reads it in chunks via the evaluate_chunk action, reassembling the full
|
|
261
|
+
// content — transparent to the caller.
|
|
260
262
|
const CHUNK_THRESHOLD = 6000;
|
|
261
263
|
|
|
262
264
|
const maybeChunk = (data) => {
|
|
@@ -269,7 +271,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
269
271
|
? ''
|
|
270
272
|
: JSON.stringify(data);
|
|
271
273
|
} catch {
|
|
272
|
-
//
|
|
274
|
+
// Not serializable (circular references, etc.) — return as-is and let the upper layer handle it via the original path
|
|
273
275
|
return { ok: true, data };
|
|
274
276
|
}
|
|
275
277
|
if (!serialized || serialized.length <= CHUNK_THRESHOLD) {
|
|
@@ -278,7 +280,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
278
280
|
const W = window;
|
|
279
281
|
if (!W.__cockpit_eval_stash_v1__) W.__cockpit_eval_stash_v1__ = new Map();
|
|
280
282
|
const stash = W.__cockpit_eval_stash_v1__;
|
|
281
|
-
// 10 min GC
|
|
283
|
+
// 10 min GC: prevents large payloads from piling up on long-lived session pages
|
|
282
284
|
const cutoff = Date.now() - 10 * 60 * 1000;
|
|
283
285
|
for (const [k, v] of stash) {
|
|
284
286
|
if (v.created < cutoff) stash.delete(k);
|
|
@@ -297,14 +299,14 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
297
299
|
};
|
|
298
300
|
};
|
|
299
301
|
|
|
300
|
-
//
|
|
301
|
-
// eval
|
|
302
|
+
// Layer 1: direct eval — covers expressions, multiple statements, IIFEs, template literals, etc.
|
|
303
|
+
// eval automatically returns the value of the last expression (similar to CDP replMode)
|
|
302
304
|
try {
|
|
303
305
|
const result = (0, eval)(code);
|
|
304
306
|
const data = result instanceof Promise ? await result : result;
|
|
305
307
|
return maybeChunk(data);
|
|
306
308
|
} catch (e1) {
|
|
307
|
-
//
|
|
309
|
+
// Layer 2: AsyncFunction fallback — covers code containing top-level await
|
|
308
310
|
try {
|
|
309
311
|
const AF = Object.getPrototypeOf(async function(){}).constructor;
|
|
310
312
|
const data = await new AF(code)();
|
|
@@ -318,7 +320,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
318
320
|
})
|
|
319
321
|
.then(results => {
|
|
320
322
|
if (message.allFrames) {
|
|
321
|
-
//
|
|
323
|
+
// Multiple frames: collect all non-undefined results
|
|
322
324
|
const all = (results || [])
|
|
323
325
|
.map((r, i) => ({ frameId: r.frameId, ...r.result }))
|
|
324
326
|
.filter(r => r.ok && r.data !== undefined);
|
|
@@ -333,7 +335,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
333
335
|
return true; // async sendResponse
|
|
334
336
|
}
|
|
335
337
|
|
|
336
|
-
//
|
|
338
|
+
// Screenshot: automation.js requests a capture of the current tab's visible area
|
|
337
339
|
if (message.type === 'cockpit:capture-tab') {
|
|
338
340
|
const tabId = sender.tab?.id;
|
|
339
341
|
if (!tabId) {
|
|
@@ -349,14 +351,15 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
349
351
|
});
|
|
350
352
|
|
|
351
353
|
// =========================================================================
|
|
352
|
-
// externally_connectable: Cockpit
|
|
354
|
+
// externally_connectable: direct communication from the Cockpit page
|
|
353
355
|
//
|
|
354
|
-
//
|
|
356
|
+
// Before setting the iframe src, BrowserBubble directly calls
|
|
355
357
|
// chrome.runtime.sendMessage(extensionId, { type: 'prepare-iframe', url })
|
|
356
|
-
//
|
|
358
|
+
// and waits for the cookie rules to be ready before rendering the iframe,
|
|
359
|
+
// completely eliminating the timing race.
|
|
357
360
|
// =========================================================================
|
|
358
361
|
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
|
|
359
|
-
//
|
|
362
|
+
// Security check: only accept messages from localhost
|
|
360
363
|
if (!sender.url || !/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\//.test(sender.url)) {
|
|
361
364
|
sendResponse({ ok: false, error: 'unauthorized' });
|
|
362
365
|
return;
|
|
@@ -382,8 +385,8 @@ chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) =>
|
|
|
382
385
|
});
|
|
383
386
|
|
|
384
387
|
// =========================================================================
|
|
385
|
-
// [调试] onSendHeaders =
|
|
386
|
-
// extraHeaders =
|
|
388
|
+
// [调试] onSendHeaders = the final headers actually sent to the server, after all modifications are done
|
|
389
|
+
// extraHeaders = required in order to see the Cookie header (Chrome 79+ restriction)
|
|
387
390
|
// =========================================================================
|
|
388
391
|
chrome.webRequest.onSendHeaders.addListener(
|
|
389
392
|
(details) => {
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cockpit Bridge - Content Script
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Injected into all pages (including inside iframes), but only activates in iframes directly owned by Cockpit.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* 1.
|
|
8
|
-
* 2.
|
|
9
|
-
* 3.
|
|
6
|
+
* Features:
|
|
7
|
+
* 1. Intercept target="_blank" link clicks → postMessage notifies the parent page to create a new bubble
|
|
8
|
+
* 2. Override window.open → postMessage notifies the parent page to create a new bubble
|
|
9
|
+
* 3. Watch page URL changes (pushState/replaceState/popstate) → notify the parent page to update the current bubble's URL
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* BrowserBubble
|
|
13
|
-
* DNR
|
|
14
|
-
* background
|
|
15
|
-
* content script
|
|
11
|
+
* How Cockpit iframes are identified:
|
|
12
|
+
* BrowserBubble appends a _cockpit=1 parameter to the iframe src,
|
|
13
|
+
* a static DNR rule strips that parameter at the network layer (the server never sees it),
|
|
14
|
+
* background records frames carrying _cockpit=1 via webNavigation.onBeforeNavigate,
|
|
15
|
+
* and the content script confirms its identity by querying background with a check-frame message.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
* BrowserBubble
|
|
19
|
-
* await
|
|
17
|
+
* How cookies are pre-injected:
|
|
18
|
+
* BrowserBubble calls background's prepare-iframe directly via externally_connectable;
|
|
19
|
+
* once the await returns, the cookie rules are ready, and only then is the iframe src set. No timing race.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
(function () {
|
|
@@ -25,16 +25,16 @@
|
|
|
25
25
|
const LOG_PREFIX = '[Cockpit Bridge]';
|
|
26
26
|
|
|
27
27
|
// ====================================================================
|
|
28
|
-
//
|
|
28
|
+
// Top-level page: expose the extension ID only on Cockpit pages (for externally_connectable to use)
|
|
29
29
|
// ====================================================================
|
|
30
30
|
if (window === window.top) {
|
|
31
31
|
if (/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(window.location.origin)) {
|
|
32
|
-
//
|
|
32
|
+
// Expose the extension ID via DOM attributes (CSP-safe, no inline script needed)
|
|
33
33
|
const id = chrome.runtime.id;
|
|
34
34
|
const version = (() => { try { return chrome.runtime.getManifest().version; } catch { return 'unknown'; } })();
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
//
|
|
36
|
+
// Expose bridge info via a <meta> tag (DOM is shared across isolated worlds)
|
|
37
|
+
// Do not modify <html> attributes, to avoid React hydration mismatch
|
|
38
38
|
function injectBridgeMeta() {
|
|
39
39
|
const head = document.head || document.documentElement;
|
|
40
40
|
const meta = document.createElement('meta');
|
|
@@ -55,23 +55,23 @@
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// ====================================================================
|
|
58
|
-
// iframe
|
|
58
|
+
// Inside an iframe: query background to confirm whether this is a Cockpit iframe
|
|
59
59
|
// ====================================================================
|
|
60
60
|
|
|
61
|
-
//
|
|
61
|
+
// Save a reference to the real parent (before the disguise script overrides it; the isolated world is unaffected)
|
|
62
62
|
const realParent = window.parent;
|
|
63
63
|
|
|
64
|
-
// DNR
|
|
65
|
-
//
|
|
64
|
+
// DNR has already stripped the _cockpit=1 parameter at the network layer, so the content script cannot see it.
|
|
65
|
+
// Identity is always confirmed through background's cockpitFrames tracking set.
|
|
66
66
|
chrome.runtime.sendMessage({ type: 'cockpit:check-frame' }, (response) => {
|
|
67
|
-
if (chrome.runtime.lastError) return; //
|
|
67
|
+
if (chrome.runtime.lastError) return; // Extension not ready yet, ignore
|
|
68
68
|
if (response?.isCockpit) {
|
|
69
69
|
activateCockpitBridge();
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
// ====================================================================
|
|
74
|
-
// Bridge
|
|
74
|
+
// Bridge activation logic
|
|
75
75
|
// ====================================================================
|
|
76
76
|
function activateCockpitBridge() {
|
|
77
77
|
console.log(LOG_PREFIX,
|
|
@@ -83,29 +83,29 @@
|
|
|
83
83
|
);
|
|
84
84
|
|
|
85
85
|
// ----------------------------------------------------------------
|
|
86
|
-
// 0a.
|
|
87
|
-
// CSP
|
|
86
|
+
// 0a. Disguise as the top-level window (inject an external script into the main world, bypassing CSP)
|
|
87
|
+
// CSP allows chrome-extension:// origins, so an external script is used instead of an inline one
|
|
88
88
|
// ----------------------------------------------------------------
|
|
89
89
|
const disguiseScript = document.createElement('script');
|
|
90
90
|
disguiseScript.src = chrome.runtime.getURL('disguise.js');
|
|
91
91
|
(document.documentElement || document).prepend(disguiseScript);
|
|
92
92
|
disguiseScript.onload = () => disguiseScript.remove();
|
|
93
93
|
|
|
94
|
-
//
|
|
94
|
+
// Inject network interception into the Main World (must run before any page scripts)
|
|
95
95
|
const networkScript = document.createElement('script');
|
|
96
96
|
networkScript.src = chrome.runtime.getURL('network-capture.js');
|
|
97
97
|
(document.documentElement || document).prepend(networkScript);
|
|
98
98
|
networkScript.onload = () => networkScript.remove();
|
|
99
99
|
|
|
100
100
|
// ----------------------------------------------------------------
|
|
101
|
-
// 1.
|
|
102
|
-
//
|
|
101
|
+
// 1. Supplemental cookie injection: the domain may change after SPA navigation, so notify background to add rules
|
|
102
|
+
// Cookies for the initial load were already pre-injected via externally_connectable
|
|
103
103
|
// ----------------------------------------------------------------
|
|
104
104
|
|
|
105
105
|
const COCKPIT_MSG_PREFIX = 'cockpit:';
|
|
106
106
|
|
|
107
107
|
// ----------------------------------------------------------------
|
|
108
|
-
// 2.
|
|
108
|
+
// 2. Send messages to the parent page (using the saved real parent reference)
|
|
109
109
|
// ----------------------------------------------------------------
|
|
110
110
|
function notifyParent(type, data) {
|
|
111
111
|
try {
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
// ----------------------------------------------------------------
|
|
121
|
-
// 3.
|
|
121
|
+
// 3. Intercept <a target="_blank"> clicks
|
|
122
122
|
// ----------------------------------------------------------------
|
|
123
123
|
document.addEventListener(
|
|
124
124
|
'click',
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
);
|
|
145
145
|
|
|
146
146
|
// ----------------------------------------------------------------
|
|
147
|
-
// 4.
|
|
147
|
+
// 4. Override window.open → intercept and notify
|
|
148
148
|
// ----------------------------------------------------------------
|
|
149
149
|
const originalOpen = window.open;
|
|
150
150
|
window.open = function (url, target, features) {
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
};
|
|
164
164
|
|
|
165
165
|
// ----------------------------------------------------------------
|
|
166
|
-
// 5.
|
|
166
|
+
// 5. Watch in-page navigation (SPA pushState / replaceState / popstate)
|
|
167
167
|
// ----------------------------------------------------------------
|
|
168
168
|
let lastUrl = window.location.href;
|
|
169
169
|
|
|
@@ -173,10 +173,10 @@
|
|
|
173
173
|
console.log(LOG_PREFIX, 'URL 变化:', lastUrl, '→', currentUrl);
|
|
174
174
|
lastUrl = currentUrl;
|
|
175
175
|
notifyParent('navigate', { url: currentUrl });
|
|
176
|
-
// URL
|
|
176
|
+
// Re-inject cookies when the URL changes (cookies may differ per domain or path)
|
|
177
177
|
try {
|
|
178
178
|
chrome.runtime.sendMessage({ type: 'cockpit:inject-cookies', url: currentUrl });
|
|
179
|
-
} catch (e) { /*
|
|
179
|
+
} catch (e) { /* ignore */ }
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
window.addEventListener('hashchange', checkUrlChange);
|
|
198
198
|
|
|
199
199
|
// ----------------------------------------------------------------
|
|
200
|
-
// 6.
|
|
200
|
+
// 6. Notify the current URL once the page has finished loading
|
|
201
201
|
// ----------------------------------------------------------------
|
|
202
202
|
if (document.readyState === 'loading') {
|
|
203
203
|
document.addEventListener('DOMContentLoaded', function () {
|
|
@@ -208,9 +208,9 @@
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// ----------------------------------------------------------------
|
|
211
|
-
// 7.
|
|
212
|
-
//
|
|
213
|
-
// automation.js
|
|
211
|
+
// 7. Load the automation layer (automation.js) for CLI control
|
|
212
|
+
// Loaded via import() in the isolated world (keeps chrome.runtime access)
|
|
213
|
+
// automation.js listens for cockpit:cmd postMessages, performs DOM operations, and returns results
|
|
214
214
|
// ----------------------------------------------------------------
|
|
215
215
|
import(chrome.runtime.getURL('automation.js'))
|
|
216
216
|
.then(mod => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Disguise as the top-level window (injected into the iframe's main world)
|
|
3
|
+
* so the site's JS cannot detect that it is running inside an iframe.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Loaded by content.js via <script src="chrome-extension://xxx/disguise.js">,
|
|
6
|
+
* bypassing CSP restrictions on inline scripts.
|
|
7
7
|
*/
|
|
8
8
|
(function () {
|
|
9
9
|
try {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Network Capture - Main World
|
|
2
|
+
* Network Capture - Main World network interception
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Injected into the iframe's main world (same way as disguise.js),
|
|
5
|
+
* intercepts the real fetch / XMLHttpRequest, and sends captured
|
|
6
|
+
* request entries back to automation.js in the Isolated World via CustomEvent.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* Main → Isolated: cockpit:network-entry { ...entry }
|
|
10
|
-
* Main → Isolated: cockpit:network-update { id, status, ... }
|
|
8
|
+
* Communication protocol:
|
|
9
|
+
* Main → Isolated: cockpit:network-entry { ...entry } sent immediately when the request starts (placeholder)
|
|
10
|
+
* Main → Isolated: cockpit:network-update { id, status, ... } sent when the response completes (fills in the fields)
|
|
11
11
|
* Isolated → Main: cockpit:network-recording { active, filters }
|
|
12
|
-
* Isolated → Main: cockpit:network-bridge-ready (
|
|
12
|
+
* Isolated → Main: cockpit:network-bridge-ready (triggers buffer flush)
|
|
13
13
|
*/
|
|
14
14
|
(function () {
|
|
15
15
|
'use strict';
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
let networkReqId = 0;
|
|
18
18
|
const MAX_BODY_SIZE = 128 * 1024;
|
|
19
19
|
|
|
20
|
-
//
|
|
20
|
+
// Recording state (synced over from automation.js in the Isolated World)
|
|
21
21
|
let recording = { active: false, filters: {} };
|
|
22
22
|
|
|
23
|
-
//
|
|
23
|
+
// Buffer entries until the bridge is ready, so entries aren't lost while automation.js is still importing
|
|
24
24
|
let entryBuffer = [];
|
|
25
25
|
let bridgeReady = false;
|
|
26
26
|
|
|
27
|
-
// ──
|
|
27
|
+
// ── Event listeners ─────────────────────────────────────
|
|
28
28
|
|
|
29
29
|
window.addEventListener('cockpit:network-recording', function (e) {
|
|
30
30
|
recording = e.detail;
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
// ──
|
|
41
|
+
// ── Utility functions ───────────────────────────────────
|
|
42
42
|
|
|
43
43
|
function shouldCaptureBody(method, url, status) {
|
|
44
44
|
if (!recording.active) return false;
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
window.dispatchEvent(new CustomEvent('cockpit:network-update', { detail: update }));
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
// ── Fetch
|
|
77
|
+
// ── Fetch interception ──────────────────────────────────
|
|
78
78
|
|
|
79
79
|
var originalFetch = window.fetch;
|
|
80
80
|
window.fetch = async function () {
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
responseHeaders: null, responseBody: null, responseSize: null,
|
|
99
99
|
recorded: wantBody,
|
|
100
100
|
};
|
|
101
|
-
//
|
|
101
|
+
// Emit a placeholder entry immediately to preserve request start order
|
|
102
102
|
emitEntry(entry);
|
|
103
103
|
|
|
104
104
|
try {
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
}
|
|
132
132
|
};
|
|
133
133
|
|
|
134
|
-
// ── XMLHttpRequest
|
|
134
|
+
// ── XMLHttpRequest interception ─────────────────────────
|
|
135
135
|
|
|
136
136
|
var XHROpen = XMLHttpRequest.prototype.open;
|
|
137
137
|
var XHRSend = XMLHttpRequest.prototype.send;
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
responseHeaders: null, responseBody: null, responseSize: null,
|
|
164
164
|
recorded: wantBody,
|
|
165
165
|
};
|
|
166
|
-
//
|
|
166
|
+
// Emit a placeholder entry immediately to preserve request start order
|
|
167
167
|
emitEntry(entry);
|
|
168
168
|
var entryMethod = entry.method;
|
|
169
169
|
var entryUrl = entry.url;
|