@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.
Files changed (215) hide show
  1. package/.next-prod/BUILD_ID +1 -1
  2. package/.next-prod/app-path-routes-manifest.json +2 -2
  3. package/.next-prod/build-manifest.json +3 -3
  4. package/.next-prod/prerender-manifest.json +3 -3
  5. package/.next-prod/react-loadable-manifest.json +7 -0
  6. package/.next-prod/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next-prod/server/app/_global-error.html +1 -1
  9. package/.next-prod/server/app/_global-error.rsc +1 -1
  10. package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  12. package/.next-prod/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  13. package/.next-prod/server/app/_global-error.segments/_head.segment.rsc +1 -1
  14. package/.next-prod/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next-prod/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next-prod/server/app/_not-found.html +2 -2
  19. package/.next-prod/server/app/_not-found.rsc +2 -2
  20. package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +2 -2
  21. package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +2 -2
  23. package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/.next-prod/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  26. package/.next-prod/server/app/api/agent/test/route.js.nft.json +1 -1
  27. package/.next-prod/server/app/api/bash/route.js.nft.json +1 -1
  28. package/.next-prod/server/app/api/chat/codex/route.js +1 -1
  29. package/.next-prod/server/app/api/chat/codex/route.js.nft.json +1 -1
  30. package/.next-prod/server/app/api/chat/deepseek/route.js +1 -1
  31. package/.next-prod/server/app/api/chat/deepseek/route.js.nft.json +1 -1
  32. package/.next-prod/server/app/api/chat/kimi/route.js +1 -1
  33. package/.next-prod/server/app/api/chat/kimi/route.js.nft.json +1 -1
  34. package/.next-prod/server/app/api/chat/ollama/route.js +1 -1
  35. package/.next-prod/server/app/api/chat/ollama/route.js.nft.json +1 -1
  36. package/.next-prod/server/app/api/chat/pty-input/route.js.nft.json +1 -1
  37. package/.next-prod/server/app/api/chat/route.js +1 -1
  38. package/.next-prod/server/app/api/chat/route.js.nft.json +1 -1
  39. package/.next-prod/server/app/api/chat/stop/route.js.nft.json +1 -1
  40. package/.next-prod/server/app/api/claude-stats/route.js.nft.json +1 -1
  41. package/.next-prod/server/app/api/commands/route.js.nft.json +1 -1
  42. package/.next-prod/server/app/api/comments/route.js.nft.json +1 -1
  43. package/.next-prod/server/app/api/db/columns/route.js.nft.json +1 -1
  44. package/.next-prod/server/app/api/db/connect/route.js.nft.json +1 -1
  45. package/.next-prod/server/app/api/db/disconnect/route.js.nft.json +1 -1
  46. package/.next-prod/server/app/api/db/export/route.js.nft.json +1 -1
  47. package/.next-prod/server/app/api/db/query/route.js.nft.json +1 -1
  48. package/.next-prod/server/app/api/db/schemas/route.js.nft.json +1 -1
  49. package/.next-prod/server/app/api/dev/spans/route.js.nft.json +1 -1
  50. package/.next-prod/server/app/api/extension/version/route.js.nft.json +1 -1
  51. package/.next-prod/server/app/api/file/route.js.nft.json +1 -1
  52. package/.next-prod/server/app/api/files/blame/route.js.nft.json +1 -1
  53. package/.next-prod/server/app/api/files/clipboard/route.js.nft.json +1 -1
  54. package/.next-prod/server/app/api/files/copy/route.js.nft.json +1 -1
  55. package/.next-prod/server/app/api/files/delete/route.js.nft.json +1 -1
  56. package/.next-prod/server/app/api/files/expanded/route.js.nft.json +1 -1
  57. package/.next-prod/server/app/api/files/index/route.js.nft.json +1 -1
  58. package/.next-prod/server/app/api/files/init/route.js.nft.json +1 -1
  59. package/.next-prod/server/app/api/files/paste/route.js.nft.json +1 -1
  60. package/.next-prod/server/app/api/files/read/route.js.nft.json +1 -1
  61. package/.next-prod/server/app/api/files/readdir/route.js.nft.json +1 -1
  62. package/.next-prod/server/app/api/files/recent/route.js.nft.json +1 -1
  63. package/.next-prod/server/app/api/files/save/route.js.nft.json +1 -1
  64. package/.next-prod/server/app/api/files/search/route.js.nft.json +1 -1
  65. package/.next-prod/server/app/api/files/stat/route.js.nft.json +1 -1
  66. package/.next-prod/server/app/api/files/text/route.js.nft.json +1 -1
  67. package/.next-prod/server/app/api/git/branch-diff/route.js.nft.json +1 -1
  68. package/.next-prod/server/app/api/git/branches/route.js.nft.json +1 -1
  69. package/.next-prod/server/app/api/git/commit-diff/route.js.nft.json +1 -1
  70. package/.next-prod/server/app/api/git/commits/route.js.nft.json +1 -1
  71. package/.next-prod/server/app/api/git/current-branch/route.js.nft.json +1 -1
  72. package/.next-prod/server/app/api/git/diff/route.js.nft.json +1 -1
  73. package/.next-prod/server/app/api/git/discard/route.js.nft.json +1 -1
  74. package/.next-prod/server/app/api/git/stage/route.js.nft.json +1 -1
  75. package/.next-prod/server/app/api/git/status/route.js.nft.json +1 -1
  76. package/.next-prod/server/app/api/git/unstage/route.js.nft.json +1 -1
  77. package/.next-prod/server/app/api/git/worktree/route.js.nft.json +1 -1
  78. package/.next-prod/server/app/api/global-state/route.js +1 -1
  79. package/.next-prod/server/app/api/global-state/route.js.nft.json +1 -1
  80. package/.next-prod/server/app/api/health/route.js.nft.json +1 -1
  81. package/.next-prod/server/app/api/jupyter/load/route.js.nft.json +1 -1
  82. package/.next-prod/server/app/api/jupyter/save/route.js.nft.json +1 -1
  83. package/.next-prod/server/app/api/jupyter/shutdown/route.js.nft.json +1 -1
  84. package/.next-prod/server/app/api/lsp/definition/route.js.nft.json +1 -1
  85. package/.next-prod/server/app/api/lsp/hover/route.js.nft.json +1 -1
  86. package/.next-prod/server/app/api/lsp/references/route.js.nft.json +1 -1
  87. package/.next-prod/server/app/api/lsp/status/route.js.nft.json +1 -1
  88. package/.next-prod/server/app/api/lsp/warmup/route.js.nft.json +1 -1
  89. package/.next-prod/server/app/api/mysql/columns/route.js.nft.json +1 -1
  90. package/.next-prod/server/app/api/mysql/connect/route.js.nft.json +1 -1
  91. package/.next-prod/server/app/api/mysql/disconnect/route.js.nft.json +1 -1
  92. package/.next-prod/server/app/api/mysql/export/route.js.nft.json +1 -1
  93. package/.next-prod/server/app/api/mysql/query/route.js.nft.json +1 -1
  94. package/.next-prod/server/app/api/mysql/schemas/route.js.nft.json +1 -1
  95. package/.next-prod/server/app/api/neo4j/connect/route.js.nft.json +1 -1
  96. package/.next-prod/server/app/api/neo4j/disconnect/route.js.nft.json +1 -1
  97. package/.next-prod/server/app/api/neo4j/query/route.js.nft.json +1 -1
  98. package/.next-prod/server/app/api/neo4j/schema/route.js.nft.json +1 -1
  99. package/.next-prod/server/app/api/note/route.js.nft.json +1 -1
  100. package/.next-prod/server/app/api/ollama/models/route.js.nft.json +1 -1
  101. package/.next-prod/server/app/api/ollama/start/route.js.nft.json +1 -1
  102. package/.next-prod/server/app/api/open-cursor/route.js.nft.json +1 -1
  103. package/.next-prod/server/app/api/open-vscode/route.js.nft.json +1 -1
  104. package/.next-prod/server/app/api/pick-folder/route.js.nft.json +1 -1
  105. package/.next-prod/server/app/api/pinned-sessions/route.js.nft.json +1 -1
  106. package/.next-prod/server/app/api/project-settings/route.js.nft.json +1 -1
  107. package/.next-prod/server/app/api/project-state/route.js.nft.json +1 -1
  108. package/.next-prod/server/app/api/projectGraph/affected/route.js.nft.json +1 -1
  109. package/.next-prod/server/app/api/projectGraph/callees/route.js.nft.json +1 -1
  110. package/.next-prod/server/app/api/projectGraph/callers/route.js.nft.json +1 -1
  111. package/.next-prod/server/app/api/projectGraph/coedit/route.js.nft.json +1 -1
  112. package/.next-prod/server/app/api/projectGraph/context/route.js.nft.json +1 -1
  113. package/.next-prod/server/app/api/projectGraph/file/route.js.nft.json +1 -1
  114. package/.next-prod/server/app/api/projectGraph/file-functions/route.js.nft.json +1 -1
  115. package/.next-prod/server/app/api/projectGraph/impact/route.js.nft.json +1 -1
  116. package/.next-prod/server/app/api/projectGraph/related/route.js.nft.json +1 -1
  117. package/.next-prod/server/app/api/projectGraph/risk/route.js.nft.json +1 -1
  118. package/.next-prod/server/app/api/projectGraph/search/route.js.nft.json +1 -1
  119. package/.next-prod/server/app/api/projects/route.js.nft.json +1 -1
  120. package/.next-prod/server/app/api/push/public-key/route.js +1 -1
  121. package/.next-prod/server/app/api/push/public-key/route.js.nft.json +1 -1
  122. package/.next-prod/server/app/api/push/subscribe/route.js +1 -1
  123. package/.next-prod/server/app/api/push/subscribe/route.js.nft.json +1 -1
  124. package/.next-prod/server/app/api/push/unsubscribe/route.js +1 -1
  125. package/.next-prod/server/app/api/push/unsubscribe/route.js.nft.json +1 -1
  126. package/.next-prod/server/app/api/redis/command/route.js.nft.json +1 -1
  127. package/.next-prod/server/app/api/redis/connect/route.js.nft.json +1 -1
  128. package/.next-prod/server/app/api/redis/delete/route.js.nft.json +1 -1
  129. package/.next-prod/server/app/api/redis/disconnect/route.js.nft.json +1 -1
  130. package/.next-prod/server/app/api/redis/get/route.js.nft.json +1 -1
  131. package/.next-prod/server/app/api/redis/keys/route.js.nft.json +1 -1
  132. package/.next-prod/server/app/api/redis/set/route.js.nft.json +1 -1
  133. package/.next-prod/server/app/api/review/[id]/comments/route.js.nft.json +1 -1
  134. package/.next-prod/server/app/api/review/[id]/replies/route.js.nft.json +1 -1
  135. package/.next-prod/server/app/api/review/[id]/route.js.nft.json +1 -1
  136. package/.next-prod/server/app/api/review/identify/route.js.nft.json +1 -1
  137. package/.next-prod/server/app/api/review/order/route.js.nft.json +1 -1
  138. package/.next-prod/server/app/api/review/route.js.nft.json +1 -1
  139. package/.next-prod/server/app/api/review/share-info/route.js.nft.json +1 -1
  140. package/.next-prod/server/app/api/review/users/route.js.nft.json +1 -1
  141. package/.next-prod/server/app/api/scheduled-tasks/route.js +1 -1
  142. package/.next-prod/server/app/api/scheduled-tasks/route.js.nft.json +1 -1
  143. package/.next-prod/server/app/api/services/config/route.js.nft.json +1 -1
  144. package/.next-prod/server/app/api/services/scripts/route.js.nft.json +1 -1
  145. package/.next-prod/server/app/api/session/[sessionId]/fork/route.js.nft.json +1 -1
  146. package/.next-prod/server/app/api/session/[sessionId]/history/route.js.nft.json +1 -1
  147. package/.next-prod/server/app/api/session-by-path/route.js.nft.json +1 -1
  148. package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js.nft.json +1 -1
  149. package/.next-prod/server/app/api/sessions/projects/route.js.nft.json +1 -1
  150. package/.next-prod/server/app/api/sessions/route.js.nft.json +1 -1
  151. package/.next-prod/server/app/api/settings/route.js.nft.json +1 -1
  152. package/.next-prod/server/app/api/skills/[id]/route.js.nft.json +1 -1
  153. package/.next-prod/server/app/api/skills/content/route.js.nft.json +1 -1
  154. package/.next-prod/server/app/api/skills/route.js.nft.json +1 -1
  155. package/.next-prod/server/app/api/terminal/aliases/route.js.nft.json +1 -1
  156. package/.next-prod/server/app/api/terminal/autocomplete/route.js.nft.json +1 -1
  157. package/.next-prod/server/app/api/terminal/bubble-order/route.js.nft.json +1 -1
  158. package/.next-prod/server/app/api/terminal/env/route.js.nft.json +1 -1
  159. package/.next-prod/server/app/api/terminal/history/route.js.nft.json +1 -1
  160. package/.next-prod/server/app/api/version/route.js.nft.json +1 -1
  161. package/.next-prod/server/app/favicon.ico/route.js.nft.json +1 -1
  162. package/.next-prod/server/app/m/page.js +2 -2
  163. package/.next-prod/server/app/m/page.js.nft.json +1 -1
  164. package/.next-prod/server/app/m/page_client-reference-manifest.js +1 -1
  165. package/.next-prod/server/app/manifest.webmanifest/route.js.nft.json +1 -1
  166. package/.next-prod/server/app/page.js +1 -1
  167. package/.next-prod/server/app/page.js.nft.json +1 -1
  168. package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
  169. package/.next-prod/server/app/project/page.js +1 -1
  170. package/.next-prod/server/app/project/page.js.nft.json +1 -1
  171. package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
  172. package/.next-prod/server/app/review/[id]/page.js +2 -2
  173. package/.next-prod/server/app/review/[id]/page.js.nft.json +1 -1
  174. package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
  175. package/.next-prod/server/app-paths-manifest.json +2 -2
  176. package/.next-prod/server/chunks/{1035.js → 5897.js} +2 -2
  177. package/.next-prod/server/chunks/9658.js +26 -26
  178. package/.next-prod/server/chunks/9830.js +1 -1
  179. package/.next-prod/server/middleware-build-manifest.js +1 -1
  180. package/.next-prod/server/middleware-react-loadable-manifest.js +1 -1
  181. package/.next-prod/server/pages/404.html +2 -2
  182. package/.next-prod/server/pages/500.html +1 -1
  183. package/.next-prod/server/server-reference-manifest.json +1 -1
  184. package/.next-prod/static/chunks/{2625-bc4748f0cd7db618.js → 1332-5e141e2acf28015a.js} +10 -10
  185. package/.next-prod/static/chunks/{217-92f800c2c438c550.js → 217-e929c46c0bd599a7.js} +2 -2
  186. package/.next-prod/static/chunks/2502-fb8682bc7a61e04d.js +4 -0
  187. package/.next-prod/static/chunks/4523.3f277a24fbcfde6d.js +138 -0
  188. package/.next-prod/static/chunks/9304-ddde9008da31b2ce.js +1 -0
  189. package/.next-prod/static/chunks/app/{layout-ac2f1f67df01cc89.js → layout-d202f8e0bcc90661.js} +1 -1
  190. package/.next-prod/static/chunks/app/m/page-71570a066f3eacf8.js +1 -0
  191. package/.next-prod/static/chunks/app/{page-1f0a6f49656a4c52.js → page-814b50b5b440249d.js} +1 -1
  192. package/.next-prod/static/chunks/app/project/{page-1f0a6f49656a4c52.js → page-814b50b5b440249d.js} +1 -1
  193. package/.next-prod/static/chunks/app/review/[id]/{page-39fa3a843adf617e.js → page-0c5d03d65758c7dc.js} +1 -1
  194. package/.next-prod/static/chunks/webpack-2c71a79c277961bc.js +1 -0
  195. package/.next-prod/trace +13 -13
  196. package/.next-prod/trace-build +1 -1
  197. package/bin/cock-browser.mjs +12 -9
  198. package/bin/cock.mjs +5 -5
  199. package/bin/postinstall.mjs +6 -6
  200. package/chrome-extension/automation.js +45 -40
  201. package/chrome-extension/background.js +70 -67
  202. package/chrome-extension/content.js +38 -38
  203. package/chrome-extension/disguise.js +4 -4
  204. package/chrome-extension/network-capture.js +16 -16
  205. package/dist/{chunk-VHFWPI2X.mjs → chunk-3M3JSZ5R.mjs} +28 -1
  206. package/dist/scheduledTasks.mjs +1 -1
  207. package/dist/wsServer.mjs +6 -31
  208. package/package.json +1 -1
  209. package/server.mjs +122 -26
  210. package/.next-prod/server/chunks/7003.js +0 -1
  211. package/.next-prod/static/chunks/471-504b396c8302f4f2.js +0 -4
  212. package/.next-prod/static/chunks/app/m/page-8b1af6f31b60c485.js +0 -1
  213. package/.next-prod/static/chunks/webpack-dbba7fa77ee1faa4.js +0 -1
  214. /package/.next-prod/static/{WfNQMmytl62j_8KtgCW9_ → JfwcuC5YjyhQWeQD-4Gvn}/_buildManifest.js +0 -0
  215. /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 注入:用 declarativeNetRequest 动态规则在网络层注入 Cookie
5
- * 不修改全局 Cookie 存储,只在请求层面补上 Cookie
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
- // 思路:iframe 跨站请求不带 SameSite=Lax Cookie。
12
- // chrome.cookies.getAll 读取目标域名的全部 Cookie,
13
- // 通过 declarativeNetRequest 动态规则在网络层 set Cookie 请求头。
14
- // 不修改 Cookie 存储,只影响请求头。
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
- // 动态规则用 requestDomains 限定域名,tabIds 限定 Cockpit 标签页,
17
- // resourceTypes 覆盖 sub_frame + 子资源(XHR/script/css/image 等)。
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
- // 启动时清理上次残留的 session 规则(插件重载后内存中的 map 丢失,但规则仍生效)
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 追踪 + Cookie 注入时序保证
30
+ // Cockpit iframe tracking + cookie injection ordering guarantee
31
31
  //
32
- // 双层保证机制:
33
- // Layer 1: externally_connectableBrowserBubble → background 直连)
34
- // → await prepareCookies() 返回后才设置 iframe src
35
- // → Cookie 规则 100% 在首次请求前就绪
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(兜底 + frame 追踪)
38
- // → 记录带 _cockpit=1 frame,供 content script check-frame 查询
39
- // → 同时触发 injectCookiesForUrl 作为兜底(刷新场景等)
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 静态规则 #3 在网络层剥离 _cockpit=1 参数,服务端永远看不到。
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
- // 只关注 iframe(frameId > 0
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
- // 兜底注入:仅在 externally_connectable 未预创建规则时才触发
55
- // 避免重复调用 injectCookiesForUrl 导致「先删旧规则异步创建新规则」的间隙
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 关闭时清理该 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
- // 已注入 Cookie 的域名 → rule ID 列表映射
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
- // 读取该域名及所有父域名的 Cookie
100
- // 例如 api.github.com 需要收集:
101
- // domain=api.github.com (精确匹配)
102
- // domain=.github.com (父域名,通过 getAll({ domain: 'github.com' }) 拿到)
103
- // domain=.com 不需要(公共后缀,也不会有 Cookie)
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
- // 去重收集所有 Cookie
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
- // 验证 Cookie domain 确实能匹配当前域名
115
- // .github.com 匹配 api.github.com ✓
116
- // .example.com 不匹配 api.github.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
- // 筛选被浏览器拦截的 Cookie(Lax/Strict/未设置)
130
- // 浏览器只自动发送 SameSite=None 的,其余需要我们追加
131
- // append 不会覆盖浏览器发的 None 版本,同名 Cookie 共存等同于正常顶层访问
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
- // Cookie 的生效域名分组
140
- // .google.com 的 Cookie → requestDomains: ['google.com'](覆盖 accounts.google.com 等所有子域名)
141
- // console.cloud.google.com 的 Cookie → requestDomains: ['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
- // 规则 A: sub_frameiframe 文档加载)
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
- // 规则 B: 子资源(XHR/script/css 等,仅 iframe 内发起)
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 查询当前 frame 是否为 Cockpit iframe(处理重定向场景)
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:在 main world 执行 JS(绕过页面 CSP 限制)
238
- // allFrames: true → 在所有 frame 中执行(解决跨域 iframe 访问问题)
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 sendResponse structured clone 边界
254
- // 存在一个隐性约 8 KiB 的截断点(复现于 session 6910d071 的 evaluate
255
- // 输出精确停在 8192 字节)。为避免大结果被默默截掉,我们在 MAIN
256
- // world 里先评估结果;如果序列化后 > CHUNK_THRESHOLD,把完整负载
257
- // 暂存到页面 window 上的一个 Map,只把一个 descriptor 返回给上层。
258
- // cock-browser CLI 会识别这个 descriptor 并通过 evaluate_chunk
259
- // action 分块读取、拼回完整内容,对调用方透明。
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:避免长会话页面堆积大 payload
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
- // 1: 直接 eval — 覆盖表达式、多语句、IIFE、模板字面量等
301
- // eval 自动返回最后一个表达式的值(类似 CDP replMode
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
- // 2: AsyncFunction fallback — 覆盖含顶层 await 的代码
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
- // frame:收集所有非 undefined 结果
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
- // 截图:automation.js 请求截取当前标签页可见区域
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
- // BrowserBubble 在设置 iframe src 前,直接调用
356
+ // Before setting the iframe src, BrowserBubble directly calls
355
357
  // chrome.runtime.sendMessage(extensionId, { type: 'prepare-iframe', url })
356
- // 等待 Cookie 规则就绪后再渲染 iframe,彻底消除时序竞争。
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
- // 安全校验:只接受 localhost 消息
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 = 才能看到 Cookie 头(Chrome 79+ 限制)
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
- * 注入到所有页面(包括 iframe 内),但只在 Cockpit 直属 iframe 中激活。
4
+ * Injected into all pages (including inside iframes), but only activates in iframes directly owned by Cockpit.
5
5
  *
6
- * 功能:
7
- * 1. 拦截 target="_blank" 链接点击 → postMessage 通知父页面创建新气泡
8
- * 2. 重写 window.open → postMessage 通知父页面创建新气泡
9
- * 3. 监听页面 URL 变化(pushState/replaceState/popstate)→ 通知父页面更新当前气泡 URL
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
- * 识别 Cockpit iframe 的方式:
12
- * BrowserBubble iframe src 上追加 _cockpit=1 参数,
13
- * DNR 静态规则在网络层剥离该参数(服务端看不到),
14
- * background 通过 webNavigation.onBeforeNavigate 记录带 _cockpit=1 frame,
15
- * content script 通过 check-frame 消息查询 background 确认身份。
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
- * Cookie 预注入的方式:
18
- * BrowserBubble 通过 externally_connectable 直接调用 background prepare-iframe
19
- * await 返回后 Cookie 规则已就绪,再设置 iframe src。无时序竞争。
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
- // 顶层页面:仅在 Cockpit 页面暴露 extension ID(供 externally_connectable 使用)
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
- // 通过 DOM 属性暴露 extension IDCSP 安全,不需要内联脚本)
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
- // 通过 <meta> 标签暴露 bridge 信息(DOM 共享,跨 isolated world)
37
- // 不修改 <html> 属性,避免 React hydration mismatch
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 内:查询 background 确认是否为 Cockpit iframe
58
+ // Inside an iframe: query background to confirm whether this is a Cockpit iframe
59
59
  // ====================================================================
60
60
 
61
- // 保存真实 parent 引用(在伪装脚本覆盖之前,isolated world 不受影响)
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 已在网络层剥离 _cockpit=1 参数,content script 看不到该参数。
65
- // 统一通过 background cockpitFrames 追踪集合来确认身份。
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. 伪装为顶层窗口(注入外部脚本到 main world,绕过 CSP
87
- // CSP 允许 chrome-extension:// 源,因此用外部脚本替代内联脚本
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
- // 注入网络拦截到 Main World(必须在页面脚本之前执行)
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. Cookie 补充注入:SPA 导航后域名可能变化,通知 background 补充规则
102
- // 首次加载的 Cookie 已由 externally_connectable 预注入
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. 向父页面发送消息(使用真实 parent 引用)
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. 拦截 <a target="_blank"> 点击
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. 重写 window.open → 拦截并通知
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. 监听页面内导航(SPA pushState / replaceState / popstate
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 变化时重新注入 Cookie(可能域名或路径 Cookie 不同)
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. 页面加载完成后通知当前 URL
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. 加载自动化层(automation.js)用于 CLI 控制
212
- // 通过 import() isolated world 中加载(保留 chrome.runtime 访问)
213
- // automation.js 监听 cockpit:cmd postMessage,执行 DOM 操作并返回结果
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
- * 伪装为顶层窗口(注入到 iframe main world
3
- * 让网站 JS 无法检测到自己在 iframe
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
- * content.js 通过 <script src="chrome-extension://xxx/disguise.js"> 加载,
6
- * 绕过 CSP 对内联脚本的限制。
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
- * 注入到 iframe main world(和 disguise.js 同方式),
5
- * 拦截真实的 fetch / XMLHttpRequest,通过 CustomEvent
6
- * 捕获的请求条目发送回 Isolated World automation.js。
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 (触发 buffer flush)
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
- // 录制状态(由 Isolated World automation.js 同步过来)
20
+ // Recording state (synced over from automation.js in the Isolated World)
21
21
  let recording = { active: false, filters: {} };
22
22
 
23
- // Bridge 就绪前缓存条目,防止 automation.js 还没 import 完时丢条目
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
- // 立即 emit 占位条目,保持请求发起顺序
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
- // 立即 emit 占位条目,保持请求发起顺序
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;