sourcemonkey 0.0.0-stage → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -2
  3. package/dist/bytes.js +37 -0
  4. package/dist/channel-placeholder.js +5 -0
  5. package/dist/collection-scan.js +17 -0
  6. package/dist/collections.html +353 -0
  7. package/dist/compare.html +358 -0
  8. package/dist/composed-source.js +205 -0
  9. package/dist/connect-prompt.html +47 -0
  10. package/dist/debug/icons.html +211 -0
  11. package/dist/debug/storage.html +163 -0
  12. package/dist/debug-log.js +75 -0
  13. package/dist/diagnostic-location.js +64 -0
  14. package/dist/eval-hook.js +308 -0
  15. package/dist/extension/background.js +5263 -0
  16. package/dist/extension/channel.js +133 -0
  17. package/dist/extension/code-links.js +187 -0
  18. package/dist/extension/code-tabs.js +460 -0
  19. package/dist/extension/collection-dialog.js +619 -0
  20. package/dist/extension/collection-display.js +322 -0
  21. package/dist/extension/collections.js +2905 -0
  22. package/dist/extension/compare.js +867 -0
  23. package/dist/extension/concurrency.js +186 -0
  24. package/dist/extension/connect-grants.js +109 -0
  25. package/dist/extension/connect-prompt-page.js +70 -0
  26. package/dist/extension/connect-prompt.js +266 -0
  27. package/dist/extension/copy-button.js +63 -0
  28. package/dist/extension/copy-dialog.js +502 -0
  29. package/dist/extension/copy-preference.js +66 -0
  30. package/dist/extension/debug/icons.js +203 -0
  31. package/dist/extension/debug/storage.js +365 -0
  32. package/dist/extension/deep-link.js +77 -0
  33. package/dist/extension/delete-dialog.js +432 -0
  34. package/dist/extension/delete-icon.js +40 -0
  35. package/dist/extension/details-tooltips.js +66 -0
  36. package/dist/extension/directories.js +679 -0
  37. package/dist/extension/export-backup.js +407 -0
  38. package/dist/extension/export-dialog.js +142 -0
  39. package/dist/extension/file-access.js +74 -0
  40. package/dist/extension/file-changes.js +346 -0
  41. package/dist/extension/frame-info.js +85 -0
  42. package/dist/extension/github-provider.js +336 -0
  43. package/dist/extension/glyph.js +84 -0
  44. package/dist/extension/gm-bindings.js +601 -0
  45. package/dist/extension/gm-download.js +419 -0
  46. package/dist/extension/gm-menu.js +228 -0
  47. package/dist/extension/gm-notification.js +141 -0
  48. package/dist/extension/gm-originator-map.js +23 -0
  49. package/dist/extension/gm-tab-data.js +121 -0
  50. package/dist/extension/gm-tab.js +160 -0
  51. package/dist/extension/gm-url-change.js +111 -0
  52. package/dist/extension/gm-url-scheme.js +27 -0
  53. package/dist/extension/gm-values.js +176 -0
  54. package/dist/extension/gm-window-control.js +104 -0
  55. package/dist/extension/gm-xhr-headers.js +333 -0
  56. package/dist/extension/gm-xhr.js +584 -0
  57. package/dist/extension/injector.js +3145 -0
  58. package/dist/extension/install-capture.js +277 -0
  59. package/dist/extension/line-diff.js +233 -0
  60. package/dist/extension/local-copy.js +560 -0
  61. package/dist/extension/local-delete.js +276 -0
  62. package/dist/extension/location-kinds.js +108 -0
  63. package/dist/extension/log-format.js +158 -0
  64. package/dist/extension/managed-changes.js +361 -0
  65. package/dist/extension/managed-items.js +57 -0
  66. package/dist/extension/match-index.js +375 -0
  67. package/dist/extension/missing-scripts.js +66 -0
  68. package/dist/extension/more-menu.js +109 -0
  69. package/dist/extension/move-dialog.js +197 -0
  70. package/dist/extension/offscreen.js +37 -0
  71. package/dist/extension/options.js +504 -0
  72. package/dist/extension/page-heading.js +170 -0
  73. package/dist/extension/page-links.js +199 -0
  74. package/dist/extension/parse-check-client.js +70 -0
  75. package/dist/extension/parse-check.js +161 -0
  76. package/dist/extension/parse-sandbox-host.js +172 -0
  77. package/dist/extension/parse-sandbox.js +175 -0
  78. package/dist/extension/popup-commands-open.js +41 -0
  79. package/dist/extension/popup-view.js +387 -0
  80. package/dist/extension/popup.js +703 -0
  81. package/dist/extension/prism-userscript.js +53 -0
  82. package/dist/extension/refresh-error.js +136 -0
  83. package/dist/extension/refresh-targets.js +160 -0
  84. package/dist/extension/related-versions.js +1144 -0
  85. package/dist/extension/resize-observer-loop.js +19 -0
  86. package/dist/extension/row-cache.js +387 -0
  87. package/dist/extension/script-blobs.js +413 -0
  88. package/dist/extension/script-details.js +658 -0
  89. package/dist/extension/script-enablement.js +285 -0
  90. package/dist/extension/script-identity.js +310 -0
  91. package/dist/extension/script-keys.js +54 -0
  92. package/dist/extension/script-log-id.js +155 -0
  93. package/dist/extension/script-log.js +290 -0
  94. package/dist/extension/script-rejection.js +104 -0
  95. package/dist/extension/script-sites.js +158 -0
  96. package/dist/extension/script-store.js +3507 -0
  97. package/dist/extension/script-updater.js +991 -0
  98. package/dist/extension/script-url-hash.js +61 -0
  99. package/dist/extension/select-all.js +37 -0
  100. package/dist/extension/select-open-guard.js +90 -0
  101. package/dist/extension/site-icon.js +1261 -0
  102. package/dist/extension/sm-dev-link.js +302 -0
  103. package/dist/extension/solo-click.js +58 -0
  104. package/dist/extension/sticky-numbers.js +188 -0
  105. package/dist/extension/storage-breakdown.js +253 -0
  106. package/dist/extension/storage-scan.js +86 -0
  107. package/dist/extension/storage-usage.js +194 -0
  108. package/dist/extension/structural-keys.js +29 -0
  109. package/dist/extension/sw-teardown.js +36 -0
  110. package/dist/extension/tab-status.js +403 -0
  111. package/dist/extension/toolbar-summary.js +105 -0
  112. package/dist/extension/user-scripts-access.js +43 -0
  113. package/dist/extension/userscript.js +3672 -0
  114. package/dist/extension/value-store.js +204 -0
  115. package/dist/extension/version-probe-fetch.js +68 -0
  116. package/dist/extension/worker-client.js +109 -0
  117. package/dist/extension/worker-ops.js +171 -0
  118. package/dist/extension/zip-writer.js +24 -0
  119. package/dist/external-files.js +878 -0
  120. package/dist/file-loader.js +436 -0
  121. package/dist/file-locations.js +439 -0
  122. package/dist/generated-wrappers.js +5 -0
  123. package/dist/github-auth.js +148 -0
  124. package/dist/github-urls.js +46 -0
  125. package/dist/gm-api-catalog.js +217 -0
  126. package/dist/gm-connect.js +273 -0
  127. package/dist/gm-xhr-types.js +5 -0
  128. package/dist/http-read.js +142 -0
  129. package/dist/icons/icon-128.png +0 -0
  130. package/dist/icons/icon-16.png +0 -0
  131. package/dist/icons/icon-48.png +0 -0
  132. package/dist/local-file-overrides.js +558 -0
  133. package/dist/manifest.json +38 -0
  134. package/dist/markup-response.js +126 -0
  135. package/dist/match-targets.js +305 -0
  136. package/dist/menu-captions.js +22 -0
  137. package/dist/offscreen.html +39 -0
  138. package/dist/options.html +197 -0
  139. package/dist/parse-check-protocol.js +51 -0
  140. package/dist/parse-location.js +171 -0
  141. package/dist/parse-sandbox.html +56 -0
  142. package/dist/phase-timing.js +155 -0
  143. package/dist/popup.html +197 -0
  144. package/dist/public-suffixes.js +6957 -0
  145. package/dist/run-context.js +31 -0
  146. package/dist/runtime-messages.js +34 -0
  147. package/dist/sandboxed-page.js +35 -0
  148. package/dist/script-build.js +787 -0
  149. package/dist/script-display.js +383 -0
  150. package/dist/script-icon.js +43 -0
  151. package/dist/script-links.js +204 -0
  152. package/dist/script-source-url.js +214 -0
  153. package/dist/script-world.js +65 -0
  154. package/dist/sm-dev-protocol.js +84 -0
  155. package/dist/source-map.js +177 -0
  156. package/dist/stack-link.js +138 -0
  157. package/dist/styles.css +825 -0
  158. package/dist/unsafe-window-use.js +229 -0
  159. package/dist/url-match.js +734 -0
  160. package/dist/userscript-header.js +248 -0
  161. package/dist/userscript.html +1165 -0
  162. package/dist/vendor/fflate.LICENSE +21 -0
  163. package/dist/vendor/fflate.js +2692 -0
  164. package/dist/vendor/prism/prism-line-numbers.css +1 -0
  165. package/dist/vendor/prism/prism-line-numbers.js +1 -0
  166. package/dist/vendor/prism/prism.css +1 -0
  167. package/dist/vendor/prism/prism.js +1946 -0
  168. package/dist/version-compare.js +141 -0
  169. package/dist/version-probe.js +120 -0
  170. package/lib/build-info.json +1 -0
  171. package/lib/bytes.d.ts +3 -0
  172. package/lib/bytes.js +37 -0
  173. package/lib/channel-placeholder.d.ts +1 -0
  174. package/lib/channel-placeholder.js +5 -0
  175. package/lib/collection-scan.d.ts +8 -0
  176. package/lib/collection-scan.js +17 -0
  177. package/lib/composed-source.d.ts +28 -0
  178. package/lib/composed-source.js +205 -0
  179. package/lib/debug-log.d.ts +9 -0
  180. package/lib/debug-log.js +75 -0
  181. package/lib/diagnostic-location.d.ts +8 -0
  182. package/lib/diagnostic-location.js +64 -0
  183. package/lib/eval-hook.d.ts +34 -0
  184. package/lib/eval-hook.js +308 -0
  185. package/lib/external-files.d.ts +111 -0
  186. package/lib/external-files.js +878 -0
  187. package/lib/file-loader.d.ts +48 -0
  188. package/lib/file-loader.js +436 -0
  189. package/lib/file-locations.d.ts +44 -0
  190. package/lib/file-locations.js +439 -0
  191. package/lib/generated-wrappers.d.ts +11 -0
  192. package/lib/generated-wrappers.js +5 -0
  193. package/lib/github-auth.d.ts +12 -0
  194. package/lib/github-auth.js +148 -0
  195. package/lib/github-urls.d.ts +1 -0
  196. package/lib/github-urls.js +46 -0
  197. package/lib/gm-api-catalog.d.ts +24 -0
  198. package/lib/gm-api-catalog.js +217 -0
  199. package/lib/gm-connect.d.ts +34 -0
  200. package/lib/gm-connect.js +273 -0
  201. package/lib/gm-xhr-types.d.ts +68 -0
  202. package/lib/gm-xhr-types.js +5 -0
  203. package/lib/harness/browser-extensions.d.ts +73 -0
  204. package/lib/harness/browser-extensions.js +285 -0
  205. package/lib/harness/browser-lang.d.ts +20 -0
  206. package/lib/harness/browser-lang.js +86 -0
  207. package/lib/harness/browser-launch.d.ts +14 -0
  208. package/lib/harness/browser-launch.js +184 -0
  209. package/lib/harness/checks.d.ts +110 -0
  210. package/lib/harness/checks.js +321 -0
  211. package/lib/harness/cli.d.ts +2 -0
  212. package/lib/harness/cli.js +2259 -0
  213. package/lib/harness/command-args.d.ts +91 -0
  214. package/lib/harness/command-args.js +369 -0
  215. package/lib/harness/command-lines.d.ts +80 -0
  216. package/lib/harness/command-lines.js +180 -0
  217. package/lib/harness/command-list.d.ts +206 -0
  218. package/lib/harness/command-list.js +1475 -0
  219. package/lib/harness/console-commands.d.ts +463 -0
  220. package/lib/harness/console-commands.js +3216 -0
  221. package/lib/harness/download-watch.d.ts +21 -0
  222. package/lib/harness/download-watch.js +156 -0
  223. package/lib/harness/extension-backend.d.ts +41 -0
  224. package/lib/harness/extension-backend.js +100 -0
  225. package/lib/harness/extension-host.d.ts +112 -0
  226. package/lib/harness/extension-host.js +552 -0
  227. package/lib/harness/extension-launch.d.ts +67 -0
  228. package/lib/harness/extension-launch.js +285 -0
  229. package/lib/harness/extension-link.d.ts +315 -0
  230. package/lib/harness/extension-link.js +607 -0
  231. package/lib/harness/external-manager.d.ts +146 -0
  232. package/lib/harness/external-manager.js +414 -0
  233. package/lib/harness/fixture.d.ts +165 -0
  234. package/lib/harness/fixture.js +461 -0
  235. package/lib/harness/frame-sessions.d.ts +71 -0
  236. package/lib/harness/frame-sessions.js +248 -0
  237. package/lib/harness/frames.d.ts +68 -0
  238. package/lib/harness/frames.js +216 -0
  239. package/lib/harness/help-format.d.ts +17 -0
  240. package/lib/harness/help-format.js +127 -0
  241. package/lib/harness/host.d.ts +352 -0
  242. package/lib/harness/host.js +423 -0
  243. package/lib/harness/index.d.ts +8 -0
  244. package/lib/harness/index.js +10 -0
  245. package/lib/harness/input-file.d.ts +18 -0
  246. package/lib/harness/input-file.js +61 -0
  247. package/lib/harness/json-output.d.ts +565 -0
  248. package/lib/harness/json-output.js +71 -0
  249. package/lib/harness/manager-actions.d.ts +21 -0
  250. package/lib/harness/manager-actions.js +129 -0
  251. package/lib/harness/manager-commands.d.ts +142 -0
  252. package/lib/harness/manager-commands.js +1196 -0
  253. package/lib/harness/manager-fetch.d.ts +33 -0
  254. package/lib/harness/manager-fetch.js +239 -0
  255. package/lib/harness/managers.d.ts +24 -0
  256. package/lib/harness/managers.js +27 -0
  257. package/lib/harness/output-style.d.ts +125 -0
  258. package/lib/harness/output-style.js +322 -0
  259. package/lib/harness/page-commands.d.ts +134 -0
  260. package/lib/harness/page-commands.js +1141 -0
  261. package/lib/harness/page-output.d.ts +67 -0
  262. package/lib/harness/page-output.js +139 -0
  263. package/lib/harness/page-watch.d.ts +171 -0
  264. package/lib/harness/page-watch.js +607 -0
  265. package/lib/harness/parse-check-node.d.ts +3 -0
  266. package/lib/harness/parse-check-node.js +132 -0
  267. package/lib/harness/path-args.d.ts +22 -0
  268. package/lib/harness/path-args.js +148 -0
  269. package/lib/harness/playwright-chromium.d.ts +16 -0
  270. package/lib/harness/playwright-chromium.js +86 -0
  271. package/lib/harness/prepare.d.ts +168 -0
  272. package/lib/harness/prepare.js +364 -0
  273. package/lib/harness/prompt-editor.d.ts +17 -0
  274. package/lib/harness/prompt-editor.js +354 -0
  275. package/lib/harness/prompt-history.d.ts +13 -0
  276. package/lib/harness/prompt-history.js +89 -0
  277. package/lib/harness/rebuild-advice.d.ts +6 -0
  278. package/lib/harness/rebuild-advice.js +39 -0
  279. package/lib/harness/run-log.d.ts +47 -0
  280. package/lib/harness/run-log.js +140 -0
  281. package/lib/harness/run-logs.d.ts +132 -0
  282. package/lib/harness/run-logs.js +241 -0
  283. package/lib/harness/run.d.ts +194 -0
  284. package/lib/harness/run.js +1973 -0
  285. package/lib/harness/runs.d.ts +229 -0
  286. package/lib/harness/runs.js +726 -0
  287. package/lib/harness/save-html.d.ts +39 -0
  288. package/lib/harness/save-html.js +89 -0
  289. package/lib/harness/saved-pages.d.ts +120 -0
  290. package/lib/harness/saved-pages.js +275 -0
  291. package/lib/harness/script-mark.d.ts +17 -0
  292. package/lib/harness/script-mark.js +56 -0
  293. package/lib/harness/snapshot.d.ts +143 -0
  294. package/lib/harness/snapshot.js +474 -0
  295. package/lib/harness/stuck-tabs.d.ts +19 -0
  296. package/lib/harness/stuck-tabs.js +131 -0
  297. package/lib/harness/tab-refs.d.ts +20 -0
  298. package/lib/harness/tab-refs.js +80 -0
  299. package/lib/harness/tampermonkey.d.ts +71 -0
  300. package/lib/harness/tampermonkey.js +356 -0
  301. package/lib/harness/tm-ids.d.ts +18 -0
  302. package/lib/harness/tm-ids.js +88 -0
  303. package/lib/harness/violentmonkey.d.ts +34 -0
  304. package/lib/harness/violentmonkey.js +162 -0
  305. package/lib/harness/wait.d.ts +77 -0
  306. package/lib/harness/wait.js +458 -0
  307. package/lib/http-read.d.ts +30 -0
  308. package/lib/http-read.js +142 -0
  309. package/lib/local-file-overrides.d.ts +35 -0
  310. package/lib/local-file-overrides.js +558 -0
  311. package/lib/manifest.json +38 -0
  312. package/lib/markup-response.d.ts +11 -0
  313. package/lib/markup-response.js +126 -0
  314. package/lib/match-targets.d.ts +18 -0
  315. package/lib/match-targets.js +305 -0
  316. package/lib/menu-captions.d.ts +1 -0
  317. package/lib/menu-captions.js +22 -0
  318. package/lib/parse-check-protocol.d.ts +27 -0
  319. package/lib/parse-check-protocol.js +51 -0
  320. package/lib/parse-location.d.ts +12 -0
  321. package/lib/parse-location.js +171 -0
  322. package/lib/phase-timing.d.ts +12 -0
  323. package/lib/phase-timing.js +155 -0
  324. package/lib/public-suffixes.d.ts +1 -0
  325. package/lib/public-suffixes.js +6957 -0
  326. package/lib/run-context.d.ts +2 -0
  327. package/lib/run-context.js +31 -0
  328. package/lib/runtime-messages.d.ts +10 -0
  329. package/lib/runtime-messages.js +34 -0
  330. package/lib/sandboxed-page.d.ts +5 -0
  331. package/lib/sandboxed-page.js +35 -0
  332. package/lib/script-build.d.ts +111 -0
  333. package/lib/script-build.js +787 -0
  334. package/lib/script-display.d.ts +51 -0
  335. package/lib/script-display.js +383 -0
  336. package/lib/script-icon.d.ts +16 -0
  337. package/lib/script-icon.js +43 -0
  338. package/lib/script-links.d.ts +24 -0
  339. package/lib/script-links.js +204 -0
  340. package/lib/script-source-url.d.ts +8 -0
  341. package/lib/script-source-url.js +214 -0
  342. package/lib/script-world.d.ts +6 -0
  343. package/lib/script-world.js +65 -0
  344. package/lib/sm-dev-protocol.d.ts +251 -0
  345. package/lib/sm-dev-protocol.js +84 -0
  346. package/lib/source-map.d.ts +32 -0
  347. package/lib/source-map.js +177 -0
  348. package/lib/stack-link.d.ts +15 -0
  349. package/lib/stack-link.js +138 -0
  350. package/lib/unsafe-window-use.d.ts +1 -0
  351. package/lib/unsafe-window-use.js +229 -0
  352. package/lib/url-match.d.ts +40 -0
  353. package/lib/url-match.js +734 -0
  354. package/lib/userscript-header.d.ts +33 -0
  355. package/lib/userscript-header.js +248 -0
  356. package/lib/version-compare.d.ts +6 -0
  357. package/lib/version-compare.js +141 -0
  358. package/lib/version-probe.d.ts +17 -0
  359. package/lib/version-probe.js +120 -0
  360. package/package.json +55 -3
  361. package/skills/SourceMonkey-dev/SKILL.md +868 -0
  362. package/skills/SourceMonkey-dev/references/cdp.md +293 -0
  363. package/skills/SourceMonkey-dev/references/commands.md +297 -0
  364. package/skills/SourceMonkey-dev/references/other-managers.md +63 -0
  365. package/skills/SourceMonkey-dev/references/specs.md +46 -0
  366. package/skills/SourceMonkey-friction/SKILL.md +103 -0
  367. package/skills/SourceMonkey-friction/references/template.md +59 -0
@@ -0,0 +1,285 @@
1
+ // A throwaway Chromium with the package's built SourceMonkey (dist/)
2
+ // loaded: sm-dev's temp browser, the `sourcemonkey/harness`
3
+ // fixture's, and the repo's own e2e suites'. With `--manager`, a
4
+ // fetched Tampermonkey or Violentmonkey instead (manager-fetch.ts).
5
+ //
6
+ // - Playwright's Chromium, since Google Chrome (137+) ignores
7
+ // `--load-extension`. Hidden runs use Chrome's new headless mode
8
+ // (`--headless=new`), the only headless mode that loads extensions,
9
+ // with a windowed Chrome's user agent (see `windowedUserAgent`).
10
+ // - A fresh profile in a temp directory, deleted when the browser is
11
+ // closed. Its Preferences grant "Allow User Scripts" before launch,
12
+ // which Chrome otherwise leaves to a click (chrome.userScripts is
13
+ // undefined without it), and pin the extension to the toolbar.
14
+ // - Launched as a persistent context (a profile directory needs one),
15
+ // which has no Playwright `Browser`. A caller that needs one (sm-dev's
16
+ // run, the fixture's `browser`) connects over CDP to the launched
17
+ // browser's port; the e2e suites use the context as it is.
18
+ import { execFile } from 'node:child_process';
19
+ import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
20
+ import net from 'node:net';
21
+ import os from 'node:os';
22
+ import path from 'node:path';
23
+ import { fileURLToPath } from 'node:url';
24
+ import { leaveDialogsOpen } from './console-commands.js';
25
+ import { ExtensionMissingError, extensionIdFromKey, findExtensionWorker, sourceMonkeyExtensionId } from './extension-backend.js';
26
+ import { fetchedDir } from './manager-fetch.js';
27
+ import { MANAGERS, managerFlag } from './managers.js';
28
+ import { loadPlaywright, missingChromium } from './playwright-chromium.js';
29
+ import { rebuildAdvice } from './rebuild-advice.js';
30
+ import { quoted } from './output-style.js';
31
+ // dist/, beside lib/: in the checkout, and in an installed package,
32
+ // which ships both.
33
+ export const EXTENSION_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'dist');
34
+ /**
35
+ * Launches Chromium with SourceMonkey loaded, or `options.manager`.
36
+ * Throws an error saying why when it can't. It has one blank tab,
37
+ * opened after the warm-up (see `warmUp`), for a run's first tab.
38
+ */
39
+ export async function launchExtensionBrowser(options = {}) {
40
+ const manager = options.manager ?? 'sourcemonkey';
41
+ const dir = manager === 'sourcemonkey' ? EXTENSION_DIR : fetchedDir(manager);
42
+ if (!existsSync(path.join(dir, 'manifest.json'))) {
43
+ throw new Error(manager === 'sourcemonkey' ? `The built extension is missing from ${EXTENSION_DIR}. ${rebuildAdvice()}`
44
+ : `${MANAGERS[manager].label} isn't fetched. Fetch it with ${quoted(`sm-dev ${managerFlag(manager)} manager fetch`)}`);
45
+ }
46
+ const headed = options.headed === true;
47
+ const profile = mkdtempSync(path.join(os.tmpdir(), 'sm-dev-extension-'));
48
+ let context;
49
+ const close = async () => {
50
+ await context?.close().catch(() => undefined);
51
+ try {
52
+ rmSync(profile, { recursive: true, force: true });
53
+ }
54
+ catch { /* Chrome may hold a file briefly */ }
55
+ };
56
+ try {
57
+ if (manager === 'sourcemonkey')
58
+ await prepareProfile(profile, options.downloadsDir);
59
+ else
60
+ writePreferences(profile, fetchedId(dir, manager), options.downloadsDir);
61
+ // Port 0 has Chrome pick a free port and write it to the profile.
62
+ // A port picked here could be taken before Chrome binds it, and the
63
+ // run would connect to whatever took it. A port given is checked
64
+ // first, since Chrome starts without its port when it's taken.
65
+ if (options.port !== undefined && !await portFree(options.port)) {
66
+ throw new Error(`Port ${options.port} is already in use. Use ${quoted('--existing-browser')} to connect to a browser there, or pick another port.`);
67
+ }
68
+ context = await launch(profile, headed, [`--remote-debugging-port=${options.port ?? 0}`, ...(options.muted === false ? [] : ['--mute-audio']), ...(options.args ?? [])], dir, options.backForwardCache === true);
69
+ // Chrome writes the port it picked; one given, it doesn't.
70
+ const port = options.port ?? readFileSync(path.join(profile, 'DevToolsActivePort'), 'utf8').split('\n')[0].trim();
71
+ if (options.connectedElsewhere)
72
+ leaveDialogsOpen(context);
73
+ if (options.downloadsDir)
74
+ await chromeDownloads(context);
75
+ if (manager === 'sourcemonkey')
76
+ await warmUp(context);
77
+ else if (manager === 'tampermonkey')
78
+ await quietTampermonkey(context, fetchedId(dir, manager));
79
+ // The launch's first tab predates the warm-up, so scripts fail in
80
+ // it. A new one replaces it (opened first: a headed browser with
81
+ // no tab left would quit).
82
+ const stale = context.pages();
83
+ await context.newPage();
84
+ for (const page of stale)
85
+ await page.close();
86
+ return { endpoint: `http://127.0.0.1:${port}`, context, profile, close };
87
+ }
88
+ catch (err) {
89
+ await close();
90
+ if (err instanceof ExtensionMissingError)
91
+ throw new Error(`Chromium did not load the extension from ${EXTENSION_DIR}`);
92
+ throw err;
93
+ }
94
+ }
95
+ // Whether nothing listens on `port` on this machine.
96
+ function portFree(port) {
97
+ return new Promise((resolve) => {
98
+ const server = net.createServer();
99
+ server.once('error', () => resolve(false));
100
+ server.listen(port, '127.0.0.1', () => server.close(() => resolve(true)));
101
+ });
102
+ }
103
+ // The user agent a windowed Chrome of this version sends, in Chrome's
104
+ // reduced form (`Chrome/153.0.0.0`). A hidden one says HeadlessChrome,
105
+ // which some servers refuse, and the extension fetches a script's
106
+ // `@require`s and `GM_xmlhttpRequest`s with it. Only the user agent
107
+ // string changes: the client hints (`Sec-CH-UA`,
108
+ // `navigator.userAgentData`) still name HeadlessChrome. Null when the
109
+ // version can't be read; the browser's own is kept then.
110
+ async function windowedUserAgent(executable) {
111
+ const version = process.platform === 'win32'
112
+ // There `chrome.exe --version` opens a window instead of printing;
113
+ // the version is the name of a folder beside it.
114
+ ? readdirSync(path.dirname(executable)).find((name) => /^\d+\.\d+\.\d+\.\d+$/.test(name)) ?? ''
115
+ : await new Promise((resolve) => {
116
+ execFile(executable, ['--version'], { timeout: 5000 }, (err, stdout) => resolve(err ? '' : stdout));
117
+ });
118
+ const major = /(\d+)\.\d+\.\d+\.\d+/.exec(version)?.[1];
119
+ if (!major)
120
+ return null;
121
+ const platform = process.platform === 'darwin' ? 'Macintosh; Intel Mac OS X 10_15_7'
122
+ : process.platform === 'win32' ? 'Windows NT 10.0; Win64; x64'
123
+ : 'X11; Linux x86_64';
124
+ return `Mozilla/5.0 (${platform}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${major}.0.0.0 Safari/537.36`;
125
+ }
126
+ /**
127
+ * Turns off Tampermonkey's "installed" page, which it opens in a new
128
+ * tab a second or so after each start of a copy loaded with
129
+ * `--load-extension` (Chrome installs it anew each time): its
130
+ * `notification_showUpdate` setting, set before it gets that far.
131
+ */
132
+ async function quietTampermonkey(context, id) {
133
+ const ours = (w) => w.url().startsWith(`chrome-extension://${id}/`);
134
+ const worker = context.serviceWorkers().find(ours) ?? await context.waitForEvent('serviceworker', { predicate: ours, timeout: 10_000 });
135
+ await worker.evaluate(async () => {
136
+ const key = '!extdb.#config';
137
+ const config = (await chrome.storage.local.get(key))[key] ?? { origin: 'normal', value: {} };
138
+ config.value.notification_showUpdate = 'off';
139
+ await chrome.storage.local.set({ [key]: config });
140
+ });
141
+ }
142
+ // The id of a fetched manager, from the key its manifest was given.
143
+ function fetchedId(dir, manager) {
144
+ const { key } = JSON.parse(readFileSync(path.join(dir, 'manifest.json'), 'utf8'));
145
+ if (typeof key !== 'string')
146
+ throw new Error(`The extension in ${dir} has no key in its manifest. Fetch it again with ${quoted(`sm-dev --manager ${MANAGERS[manager].short} manager fetch`)}`);
147
+ return extensionIdFromKey(key);
148
+ }
149
+ async function launch(profile, headed, args = [], dir = EXTENSION_DIR, backForwardCache = false) {
150
+ const { chromium, install } = await loadPlaywright();
151
+ // Playwright's own message names only the missing path.
152
+ const missing = missingChromium(chromium.executablePath(), install);
153
+ if (missing)
154
+ throw new Error(missing);
155
+ const userAgent = headed ? null : await windowedUserAgent(chromium.executablePath()).catch(() => null);
156
+ try {
157
+ return await chromium.launchPersistentContext(profile, {
158
+ // `headless: false` keeps Playwright from adding its own headless
159
+ // flag (the old mode, which doesn't load extensions); a hidden
160
+ // browser gets `--headless=new` instead.
161
+ headless: false,
162
+ // The run closes it as it stops, on the signals it handles itself.
163
+ // Playwright's own handlers would close it first, and the run would
164
+ // report the browser gone. It still closes it if sm-dev exits.
165
+ handleSIGINT: false,
166
+ handleSIGTERM: false,
167
+ handleSIGHUP: false,
168
+ // A visible browser gets no emulated viewport, so its windows are
169
+ // sized as in a browser a person runs. Playwright's default (a
170
+ // fixed 1280x720) resizes every window it sees in a visible browser
171
+ // to fit, popups such as SourceMonkey's connection prompt included.
172
+ // A hidden one keeps the default: nobody sees its windows, and its
173
+ // pages keep the size they always had.
174
+ ...(headed ? { viewport: null } : {}),
175
+ ...(backForwardCache ? { ignoreDefaultArgs: ['--disable-back-forward-cache'] } : {}),
176
+ args: [
177
+ ...(headed ? [] : ['--headless=new']),
178
+ ...(userAgent ? [`--user-agent=${userAgent}`] : []),
179
+ ...extensionArgs(dir),
180
+ ...args,
181
+ ],
182
+ });
183
+ }
184
+ catch (err) {
185
+ // Playwright's message starts with the call's name, and may go on
186
+ // for lines (a boxed install hint); the first line says what failed.
187
+ const first = err.message.split('\n')[0].replace(/^browserType\.\w+: /, '');
188
+ throw new Error(`Chromium could not start: ${first}`);
189
+ }
190
+ }
191
+ /**
192
+ * Installs a script and removes it again, before the run opens its tab.
193
+ *
194
+ * - In a fresh profile, a tab opened before the extension's first script
195
+ * install can't message the extension, even after a reload: a script
196
+ * that runs there reports that it couldn't read its stored GM values.
197
+ * Only a tab in a new renderer process (another site) recovers.
198
+ * - The run pushes its scripts after its tab is open, so it needs a tab
199
+ * opened after this. See TODO.md.
200
+ */
201
+ export async function warmUp(context) {
202
+ const worker = await findExtensionWorker(context);
203
+ await worker.evaluate(async (text) => {
204
+ const api = self.SourceMonkey;
205
+ const run = 'sm-dev-warm-up';
206
+ await api.writeRunScript({ run, text });
207
+ await api.releaseRun(run);
208
+ }, '// ==UserScript==\n// @name sm-dev warm-up\n// @match https://sm-dev.invalid/*\n// ==/UserScript==\n');
209
+ }
210
+ /**
211
+ * Readies a new profile for SourceMonkey (Preferences, first run done),
212
+ * for a browser started with `extensionArgs`. Also for a test's own
213
+ * browser, launched outside Playwright.
214
+ */
215
+ export async function prepareProfile(profile, downloadsDir) {
216
+ writePreferences(profile, await sourceMonkeyExtensionId(), downloadsDir);
217
+ await primeFirstRun(profile);
218
+ }
219
+ /** The Chrome flags that load the package's SourceMonkey (or the extension in `dir`), and only it. */
220
+ export function extensionArgs(dir = EXTENSION_DIR) {
221
+ return [`--disable-extensions-except=${dir}`, `--load-extension=${dir}`];
222
+ }
223
+ // Grants the extension "Allow User Scripts" and pins it to the toolbar,
224
+ // with the keys Chrome writes when those are clicked. With
225
+ // `downloadsDir`, also the keys Chrome writes when the download
226
+ // location is changed in its settings: `directory_upgrade` marks it
227
+ // user-chosen, so Chrome doesn't fall back to its platform default.
228
+ function writePreferences(profile, extensionId, downloadsDir) {
229
+ const dir = path.join(profile, 'Default');
230
+ mkdirSync(dir, { recursive: true });
231
+ const prefs = {
232
+ extensions: { settings: { [extensionId]: { user_scripts_enabled: true } }, pinned_extensions: [extensionId] },
233
+ ...(downloadsDir ? { download: { default_directory: downloadsDir, directory_upgrade: true, prompt_for_download: false } } : {}),
234
+ };
235
+ writeFileSync(path.join(dir, 'Preferences'), JSON.stringify(prefs));
236
+ }
237
+ // Hands downloads back to Chrome. Playwright has told it to save every
238
+ // download under a GUID name in its own temp dir (CDP
239
+ // `Browser.setDownloadBehavior: allowAndName`), with no option to opt
240
+ // out. `default` undoes that, so the Preferences above apply.
241
+ async function chromeDownloads(context) {
242
+ const page = await context.newPage();
243
+ try {
244
+ const cdp = await context.newCDPSession(page);
245
+ await cdp.send('Browser.setDownloadBehavior', { behavior: 'default' });
246
+ await cdp.detach();
247
+ }
248
+ finally {
249
+ await page.close();
250
+ }
251
+ }
252
+ /**
253
+ * Marks the extension's first run done, in a launch of its own before
254
+ * the run's.
255
+ *
256
+ * - On a first run, the extension opens its collections page, which adds
257
+ * the default collections from GitHub. Their scripts would then run
258
+ * alongside the one being tried.
259
+ * - That starts while the browser is still starting, so the flag can't
260
+ * be set in time in the run's own launch. Set here, it's in storage
261
+ * for the next launch, whose first-run check then does nothing.
262
+ * - This launch may still have started the page's adds, so it closes the
263
+ * page and removes what they added.
264
+ */
265
+ async function primeFirstRun(profile) {
266
+ const context = await launch(profile, false);
267
+ try {
268
+ const worker = await findExtensionWorker(context);
269
+ // Key mirrors FIRST_RUN_DONE_KEY in src/extension/script-store.ts.
270
+ await worker.evaluate(() => chrome.storage.local.set({ firstRunComplete: true }));
271
+ for (const page of context.pages()) {
272
+ if (page.url().includes('/collections.html'))
273
+ await page.close().catch(() => undefined);
274
+ }
275
+ await worker.evaluate(async () => {
276
+ const sm = self.SourceMonkey;
277
+ for (const c of await sm.getCollections())
278
+ await sm.removeCollection(c.id);
279
+ });
280
+ }
281
+ finally {
282
+ // A clean close is what writes the storage to disk.
283
+ await context.close();
284
+ }
285
+ }
@@ -0,0 +1,315 @@
1
+ import type { BrowserContext, Page } from '@playwright/test';
2
+ import { type ProtocolVersion } from '../sm-dev-protocol.js';
3
+ import type { StackLayout } from '../composed-source.js';
4
+ import type { PushedFile } from '../external-files.js';
5
+ import { type LogEntry, type PushResult } from './extension-backend.js';
6
+ import type { ManagerControl } from './manager-commands.js';
7
+ import type { ExtensionFrame } from './frames.js';
8
+ /**
9
+ * How the extension's protocol version compares with sm-dev's
10
+ * (`SM_DEV_PROTOCOL`): an error for a major that differs, a note for a
11
+ * minor that does, or null when they match.
12
+ */
13
+ export declare function compareProtocol(extension: ProtocolVersion | undefined, ours?: ProtocolVersion): {
14
+ error?: string;
15
+ note?: string;
16
+ } | null;
17
+ /** An event from the extension, as sm-dev-link.ts and background.ts send them. */
18
+ export type ExtensionEvent = {
19
+ kind: 'report';
20
+ type: string;
21
+ tabId: number;
22
+ documentId: string;
23
+ topFrame?: boolean;
24
+ scriptId: string;
25
+ name: string;
26
+ url: string;
27
+ message?: string;
28
+ stack?: string;
29
+ reason?: string;
30
+ detail?: string;
31
+ at: number;
32
+ } | {
33
+ kind: 'log';
34
+ scriptId: string;
35
+ entry: LogEntry;
36
+ fromReport?: boolean;
37
+ at: number;
38
+ } | {
39
+ kind: 'menu';
40
+ op: 'add' | 'remove';
41
+ tabId: number;
42
+ documentId: string;
43
+ topFrame?: boolean;
44
+ scriptId: string;
45
+ commandId: string;
46
+ name?: string;
47
+ at: number;
48
+ } | {
49
+ kind: 'values';
50
+ scriptId: string;
51
+ tabId?: number;
52
+ documentId?: string;
53
+ writes: {
54
+ key: string;
55
+ value?: unknown;
56
+ deleted?: boolean;
57
+ }[];
58
+ at: number;
59
+ } | {
60
+ kind: 'tab-opened';
61
+ sourceTabId: number;
62
+ tabId: number;
63
+ url: string;
64
+ scriptId?: string;
65
+ requestId?: string;
66
+ active?: boolean;
67
+ at: number;
68
+ } | {
69
+ kind: 'tab-closed';
70
+ scriptId: string;
71
+ requestId: string;
72
+ at: number;
73
+ } | {
74
+ kind: 'request';
75
+ scriptId: string;
76
+ tabId: number;
77
+ documentId?: string;
78
+ id: string;
79
+ url: string;
80
+ method: string;
81
+ outcome?: 'load' | 'error' | 'timeout' | 'abort';
82
+ status?: number;
83
+ error?: string;
84
+ at: number;
85
+ } | {
86
+ kind: 'download';
87
+ scriptId: string;
88
+ tabId: number;
89
+ documentId?: string;
90
+ requestId: string;
91
+ url: string;
92
+ name: string;
93
+ outcome?: 'load' | 'error' | 'timeout' | 'abort';
94
+ filename?: string;
95
+ error?: string;
96
+ at: number;
97
+ } | {
98
+ kind: 'connect-prompt';
99
+ op: 'shown' | 'answered' | 'closed';
100
+ promptId: string;
101
+ scriptId: string;
102
+ tabId: number;
103
+ host: string;
104
+ url?: string;
105
+ method?: string;
106
+ answer?: 'once' | 'cancel' | 'allow' | 'allow-all' | 'deny';
107
+ at: number;
108
+ } | {
109
+ kind: 'notification';
110
+ op: 'shown' | 'clicked' | 'closed';
111
+ scriptId: string;
112
+ tabId: number;
113
+ documentId?: string;
114
+ requestId: string;
115
+ notificationId?: string;
116
+ text?: string;
117
+ title?: string;
118
+ at: number;
119
+ };
120
+ /**
121
+ * A run's id in the extension: sm-dev's run id (`D2`, or a named one)
122
+ * with a random tail, since run ids are reused once a run ends. Kept to
123
+ * the characters a URL path segment allows, since its scripts' URLs
124
+ * carry it.
125
+ */
126
+ export declare function linkRunId(runId: string | undefined): string;
127
+ export declare class ExtensionLink {
128
+ private readonly context;
129
+ /**
130
+ * The run's unique id (`linkRunId`), which its claims and its test
131
+ * collection carry.
132
+ */
133
+ readonly runId: string;
134
+ /** The extension's service worker, whose console carries the events. */
135
+ readonly workerUrl: string;
136
+ /** The origin of the extension's pages and its scripts' world. */
137
+ readonly origin: string;
138
+ private worker;
139
+ private readonly heard;
140
+ private readonly stopped;
141
+ private readonly listeners;
142
+ private readonly restartListeners;
143
+ private renewTimer;
144
+ private readonly tabs;
145
+ private readonly vouches;
146
+ private closed;
147
+ private closing;
148
+ private readonly renewing;
149
+ private adopted;
150
+ /** The extension's protocol version, as `open` read it. */
151
+ private extensionProtocol;
152
+ /** For the run to print: how the extension's protocol minor differs from sm-dev's, if it does. */
153
+ versionNote: string | null;
154
+ private absent;
155
+ private madeUpTabId;
156
+ private readonly onWorker;
157
+ /**
158
+ * Throws, naming `feature`, when the extension's protocol minor is
159
+ * older than `minor`: for a call or event added in that minor, before
160
+ * using it.
161
+ */
162
+ requireMinor(minor: number, feature: string): void;
163
+ /**
164
+ * Whether this run has a SourceMonkey link: for output that depends
165
+ * on what the extension does. False for a run with another manager.
166
+ */
167
+ get linked(): boolean;
168
+ private constructor();
169
+ /**
170
+ * Connects to the extension in `context`'s browser, for a run. Its
171
+ * tabs run its own scripts, and it hears the extension's events.
172
+ * Throws when the extension's protocol major differs from sm-dev's
173
+ * (`compareProtocol`), or `userScriptsOff` when Chrome won't let it
174
+ * run scripts. A minor that differs leaves `versionNote` set.
175
+ */
176
+ static open(context: BrowserContext, runId: string): Promise<ExtensionLink>;
177
+ /**
178
+ * A link for a run whose manager isn't SourceMonkey: no extension to
179
+ * call, no tabs claimed, no events heard. Its tabs are plain browser
180
+ * tabs, and every call into the extension throws `reason`.
181
+ */
182
+ static without(context: BrowserContext, runId: string, reason: string): ExtensionLink;
183
+ private adopt;
184
+ /** Hears each event the extension sends. */
185
+ onEvent(listener: (event: ExtensionEvent) => void): void;
186
+ /**
187
+ * Told when a new worker replaced the one before, whose events in
188
+ * between are lost, with how many of the run's tab claims had to be
189
+ * made again.
190
+ */
191
+ onRestart(listener: (restoredClaims: number) => void): void;
192
+ private current;
193
+ /**
194
+ * Runs `fn` in the worker. Tried again once in a new worker when the
195
+ * one known stopped meanwhile; an error of the call's own is thrown,
196
+ * not repeated, since a call may not be safe to make twice.
197
+ */
198
+ call<A, R>(fn: (arg: A) => R | Promise<R>, arg: A): Promise<R>;
199
+ /**
200
+ * Claims `page`'s tab for the run before it loads the run's page. The
201
+ * tab is named to the extension by a navigation it watches for, to
202
+ * `about:blank#<fragment>`, and the claim is recorded before this
203
+ * returns. Leaves the tab on `about:blank`, for the run to load its
204
+ * page in. Returns the tab's id. Throws an error naming the step that
205
+ * failed. A link `without` SourceMonkey claims nothing, and makes up
206
+ * an id.
207
+ */
208
+ claimPage(page: Page, scriptsOff?: boolean): Promise<number>;
209
+ private nameTab;
210
+ /**
211
+ * A tab the extension claimed for the run itself (one a run tab
212
+ * opened), for renewals to keep.
213
+ */
214
+ holdTab(tabId: number, scriptsOff?: boolean): void;
215
+ /** The run no longer holds the tab (it closed). */
216
+ forgetTab(tabId: number): void;
217
+ /**
218
+ * Marks a tab the run claimed, for every document it loads from now
219
+ * on, as one whose scripts on the run's own get `run`, or takes the
220
+ * marker away. Through a CDP script, which runs ahead of every
221
+ * extension script in a new document. It only reaches the tab: the
222
+ * tabs it opens, and its subframes, ask the extension. The marker is
223
+ * gone with the session, as the run closes or its connection drops.
224
+ */
225
+ private setVouch;
226
+ /** Turns a claimed tab's scripts off or back on, for `--noscript` tabs. */
227
+ setTabScriptsOff(tabId: number, off: boolean): Promise<void>;
228
+ private forgetVouch;
229
+ private renew;
230
+ private renewIn;
231
+ /**
232
+ * Installs or replaces one of the run's scripts in its test collection,
233
+ * with the dependency bodies read from disk (`pushedFiles`).
234
+ */
235
+ /**
236
+ * `source`: the file's `file://` URL, which `script list` shows. An
237
+ * extension before protocol 1.2 ignores it.
238
+ */
239
+ push(text: string, name: string, files?: Record<string, PushedFile>, source?: string): Promise<PushResult>;
240
+ /** Which document `tabId` shows (see the worker's `tabDocument`), or null if it can't be read. */
241
+ tabDocument(tabId: number): Promise<{
242
+ url: string;
243
+ timeOrigin: number;
244
+ } | null>;
245
+ /** How the script's injected entry maps back to its files; see `remapStackText`. */
246
+ stackLayout(scriptId: string): Promise<StackLayout | null>;
247
+ /**
248
+ * A tab's frames, live documents only, as Chrome lists them to the
249
+ * extension: straight from `chrome.webNavigation`, which every
250
+ * extension sm-dev works with has, so no protocol version gates it.
251
+ * Null when Chrome can't list them (the tab is gone).
252
+ */
253
+ allFrames(tabId: number): Promise<ExtensionFrame[] | null>;
254
+ /** A script's stored values, in the wire form the extension keeps. */
255
+ values(scriptId: string): Promise<Record<string, unknown>>;
256
+ /** A script's Log entries, oldest first, as its Log tab shows them. */
257
+ scriptLog(scriptId: string): Promise<LogEntry[]>;
258
+ /** Writes values (wire form) as another tab of the script would. */
259
+ writeValues(scriptId: string, writes: {
260
+ key: string;
261
+ value?: unknown;
262
+ deleted?: boolean;
263
+ }[]): Promise<void>;
264
+ /** The scripts the toolbar badge lists for a tab, with their status and collection. */
265
+ scriptsOnTab(tabId: number): Promise<{
266
+ id: string;
267
+ name: string;
268
+ status: string;
269
+ collection: string;
270
+ number?: number;
271
+ }[]>;
272
+ /**
273
+ * Fires a menu command in a tab, as clicking it in the toolbar menu
274
+ * does: in `documentId`, the document that registered it, when given.
275
+ * An extension before protocol 2.1 ignores it, firing the first match.
276
+ */
277
+ fireMenu(tabId: number, scriptId: string, commandId: string, documentId?: string): Promise<boolean>;
278
+ /**
279
+ * Runs a manual script (`@run-at context-menu`) in a tab, as its popup
280
+ * "Run this script" does. Throws with the extension's reason when it
281
+ * can't.
282
+ */
283
+ runManualScript(tabId: number, scriptId: string): Promise<void>;
284
+ /** The collections and installed scripts, for the `collection` and `script` commands. */
285
+ readonly manager: ManagerControl;
286
+ /** Clicks a shown notification, as the person would. False when it isn't showing. */
287
+ clickNotification(notificationId: string): Promise<boolean>;
288
+ /**
289
+ * Answers a connection prompt, as the person would in its window.
290
+ * False when it isn't waiting.
291
+ */
292
+ answerConnectPrompt(promptId: string, choice: 'once' | 'cancel' | 'allow' | 'allow-all' | 'deny'): Promise<boolean>;
293
+ /** Closes a shown notification, as the person would. */
294
+ closeNotification(notificationId: string): Promise<boolean>;
295
+ /**
296
+ * Releases the run's claims and subscription, which removes its test
297
+ * collection, and stops renewing.
298
+ */
299
+ close(): Promise<void>;
300
+ private release;
301
+ }
302
+ /** An error's first line, without the call Playwright names at its start. */
303
+ export declare function firstLine(err: unknown): string;
304
+ /**
305
+ * The collection and script commands' connection to SourceMonkey, with
306
+ * no run: no tabs claimed, no events heard. For one command, made just
307
+ * after the worker is found, so it doesn't look for a new one.
308
+ * - Throws as `ExtensionLink.open` does for a protocol major that
309
+ * differs.
310
+ * - Doesn't need "Allow User Scripts", since nothing here runs a script.
311
+ */
312
+ export declare function openManagerLink(context: BrowserContext): Promise<{
313
+ manager: ManagerControl;
314
+ versionNote: string | null;
315
+ }>;