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,354 @@
1
+ // Multi-line editing for the `--interactive` prompt. readline edits one
2
+ // line, but a `{{` block is several: here the whole block is one buffer
3
+ // holding newlines, typed and edited in place, and sent once complete.
4
+ //
5
+ // - Enter inserts a newline while the buffer holds an unclosed block,
6
+ // and sends it otherwise. Alt-Enter always inserts one.
7
+ // - Lines after the first are drawn after `... `.
8
+ // - A buffer taller than the terminal is drawn as a window of rows
9
+ // around the cursor, scrolled as it moves, and drawn whole once sent.
10
+ // - Up and Down move between the buffer's lines, and step through the
11
+ // history from its first and last line. Once they have brought back
12
+ // an entry, they step whole entries until another key is pressed, so
13
+ // a block is passed over in one step. Ctrl-P / Ctrl-N always do.
14
+ // - Home / End and Ctrl-A / Ctrl-E go to the start and end of the line,
15
+ // Ctrl-U and Ctrl-K delete to them (Ctrl-Y brings it back).
16
+ // - Esc clears the buffer, as Ctrl-C does.
17
+ // - With `paint`, the prompts and the typed text are drawn in color.
18
+ //
19
+ // readline has no hooks for this. It calls these methods through the
20
+ // instance (`_ttyWrite` for each key, `_refreshLine` to redraw,
21
+ // `getCursorPos` for where the cursor is drawn), so they are replaced
22
+ // on it. Its undo and kill ring are only reachable by symbol. All are
23
+ // internal, kept by Node for compatibility; checked on Node 22.
24
+ import readline from 'node:readline';
25
+ /**
26
+ * Makes `rl` edit several lines as one buffer. `complete` says whether
27
+ * a buffer is ready to send, rather than inside an unclosed block.
28
+ * `cleared` gets the text of a buffer emptied by `clear`. `paint`
29
+ * colors the prompts and the typed text as they are drawn.
30
+ */
31
+ export function editMultiLine(rl, continuation, complete, cleared, paint) {
32
+ const r = rl;
33
+ const ttyWrite = r._ttyWrite.bind(r);
34
+ const beforeEdit = internal(r, '_beforeEdit');
35
+ const pushToKillRing = internal(r, '_pushToKillRing');
36
+ const shown = (text) => text.replaceAll('\n', '\n' + continuation);
37
+ // Screen columns, by readline's own measure of one character.
38
+ const widths = new Map();
39
+ const charWidth = (ch, col) => {
40
+ if (ch === '\t')
41
+ return r.tabSize - (col % r.tabSize);
42
+ let width = widths.get(ch);
43
+ if (width === undefined)
44
+ widths.set(ch, (width = r._getDisplayPos(ch).cols));
45
+ return width;
46
+ };
47
+ const textWidth = (text) => {
48
+ let width = 0;
49
+ for (const ch of text)
50
+ width += charWidth(ch, width);
51
+ return width;
52
+ };
53
+ // The screen rows `text` takes from column 0, as the terminal wraps
54
+ // it, and the row and column just after it. A full row is followed by
55
+ // an empty one, where the cursor goes, as readline counts it. A wide
56
+ // character that doesn't fit at a row's end starts the next row.
57
+ // `starts`: where in `text` each row starts, for painting it.
58
+ const layout = (text) => {
59
+ const width = r.columns;
60
+ const rows = [''];
61
+ const starts = [0];
62
+ let col = 0;
63
+ let at = 0;
64
+ const newRow = (start) => {
65
+ rows.push('');
66
+ starts.push(start);
67
+ col = 0;
68
+ };
69
+ for (const ch of text) {
70
+ const next = at + ch.length;
71
+ if (ch === '\n') {
72
+ newRow(next);
73
+ at = next;
74
+ continue;
75
+ }
76
+ const w = charWidth(ch, col);
77
+ if (col + w > width)
78
+ newRow(at);
79
+ rows[rows.length - 1] += ch;
80
+ col += w;
81
+ at = next;
82
+ if (col >= width)
83
+ newRow(at);
84
+ }
85
+ return { rows, starts, row: rows.length - 1, col };
86
+ };
87
+ // The rows of `text` (a prompt, then the shown buffer) in color: the
88
+ // prompt and each `... ` as prompts, the rest as typed text.
89
+ const painted = (text, rows, starts) => {
90
+ if (!paint)
91
+ return rows;
92
+ const isPrompt = new Array(text.length).fill(false);
93
+ const mark = (from, length) => {
94
+ for (let i = from; i < from + length && i < text.length; i++)
95
+ isPrompt[i] = true;
96
+ };
97
+ mark(0, r.getPrompt().length);
98
+ for (let i = text.indexOf('\n'); i >= 0; i = text.indexOf('\n', i + 1))
99
+ mark(i + 1, continuation.length);
100
+ return rows.map((row, n) => {
101
+ let out = '';
102
+ let run = '';
103
+ let prompt = false;
104
+ const flush = () => {
105
+ if (run)
106
+ out += prompt ? paint.prompt(run) : paint.text(run);
107
+ run = '';
108
+ };
109
+ let at = starts[n];
110
+ for (const ch of row) {
111
+ if (isPrompt[at] !== prompt) {
112
+ flush();
113
+ prompt = isPrompt[at];
114
+ }
115
+ run += ch;
116
+ at += ch.length;
117
+ }
118
+ flush();
119
+ return out;
120
+ });
121
+ };
122
+ // The first buffer row drawn, when it is taller than the terminal.
123
+ let top = 0;
124
+ // Set while a sent buffer is drawn whole.
125
+ let whole = false;
126
+ const cursorAt = () => layout(r.getPrompt() + shown(r.line.slice(0, r.cursor)));
127
+ // Rows count from the top row drawn, as readline and writeOutputLine
128
+ // take them.
129
+ r.getCursorPos = () => {
130
+ const at = cursorAt();
131
+ return { rows: at.row - top, cols: at.col };
132
+ };
133
+ // readline's own redraw, of the shown text and only the rows that fit.
134
+ r._refreshLine = () => {
135
+ const text = r.getPrompt() + shown(r.line);
136
+ const all = layout(text);
137
+ const at = cursorAt();
138
+ // One row spare, so the window never scrolls the terminal itself.
139
+ const height = whole ? Infinity : Math.max(1, (r.output.rows || 24) - 1);
140
+ if (all.rows.length <= height)
141
+ top = 0;
142
+ else {
143
+ if (at.row < top)
144
+ top = at.row;
145
+ if (at.row >= top + height)
146
+ top = at.row - height + 1;
147
+ top = Math.min(top, all.rows.length - height);
148
+ }
149
+ const rows = painted(text, all.rows, all.starts).slice(top, top + height);
150
+ const out = r.output;
151
+ if (r.prevRows)
152
+ readline.moveCursor(out, 0, -r.prevRows);
153
+ readline.cursorTo(out, 0);
154
+ readline.clearScreenDown(out);
155
+ // Rows are ended here rather than wrapped by the terminal, so where
156
+ // each starts doesn't depend on how the terminal wraps.
157
+ r._writeToOutput(rows.join('\r\n'));
158
+ readline.cursorTo(out, at.col);
159
+ const below = rows.length - 1 - (at.row - top);
160
+ if (below > 0)
161
+ readline.moveCursor(out, 0, -below);
162
+ r.prevRows = at.row - top;
163
+ };
164
+ const set = (text, cursor = text.length) => {
165
+ r.line = text;
166
+ r.cursor = cursor;
167
+ r._refreshLine();
168
+ };
169
+ // A change readline's undo (Ctrl-_) can take back, and deleted text
170
+ // its Ctrl-Y can bring back, as with its own edits.
171
+ const edit = (text, cursor, deleted) => {
172
+ beforeEdit(r.line, r.cursor);
173
+ if (deleted)
174
+ pushToKillRing(deleted);
175
+ set(text, cursor);
176
+ };
177
+ const insert = (text) => edit(r.line.slice(0, r.cursor) + text + r.line.slice(r.cursor), r.cursor + text.length);
178
+ // The start and end of the line holding `at` in the buffer.
179
+ const lineStart = (at = r.cursor) => (at === 0 ? 0 : r.line.lastIndexOf('\n', at - 1) + 1);
180
+ const lineEnd = (at = r.cursor) => {
181
+ const i = r.line.indexOf('\n', at);
182
+ return i < 0 ? r.line.length : i;
183
+ };
184
+ // The prompt's width before the line starting at `start`.
185
+ const promptWidth = (start) => textWidth(start === 0 ? r.getPrompt() : continuation);
186
+ // Up or Down a line, keeping the cursor's column on screen, and
187
+ // between characters. False when there is no line that way.
188
+ const moveLine = (up) => {
189
+ const start = lineStart();
190
+ if (up ? start === 0 : lineEnd() === r.line.length)
191
+ return false;
192
+ // Measured with the prompt, as tabs stop at screen columns.
193
+ const column = textWidth((start === 0 ? r.getPrompt() : continuation) + r.line.slice(start, r.cursor));
194
+ const target = up ? lineStart(start - 1) : lineEnd() + 1;
195
+ let at = target;
196
+ let col = promptWidth(target);
197
+ for (const ch of r.line.slice(target, lineEnd(target))) {
198
+ const w = charWidth(ch, col);
199
+ if (col + w > column)
200
+ break;
201
+ col += w;
202
+ at += ch.length;
203
+ }
204
+ set(r.line, at);
205
+ return true;
206
+ };
207
+ // The history, browsed here rather than by readline, which would
208
+ // replace a buffer with the text before the cursor when no older
209
+ // entry starts with it, and can't give back what was being typed.
210
+ // `index` is the entry shown, -1 for what was being typed (`draft`).
211
+ // Any other key makes the buffer, edited or not, the draft again. As
212
+ // readline does, a one-line draft only finds entries starting with
213
+ // its text before the cursor.
214
+ let index = -1;
215
+ let draft = '';
216
+ let prefix = '';
217
+ // Up / Down last brought back an entry, or the draft, and no other
218
+ // key since.
219
+ let browsing = false;
220
+ const browse = (older) => {
221
+ const entries = r.history;
222
+ if (index === -1) {
223
+ if (!older)
224
+ return;
225
+ draft = r.line;
226
+ prefix = draft.includes('\n') ? '' : draft.slice(0, r.cursor);
227
+ }
228
+ for (let i = index + (older ? 1 : -1); older ? i < entries.length : i >= 0; i += older ? 1 : -1) {
229
+ if (entries[i].startsWith(prefix) && entries[i] !== r.line) {
230
+ index = i;
231
+ browsing = true;
232
+ edit(entries[i], entries[i].length);
233
+ return;
234
+ }
235
+ }
236
+ if (!older) {
237
+ index = -1;
238
+ browsing = true;
239
+ edit(draft, draft.length);
240
+ }
241
+ };
242
+ function clear() {
243
+ if (r.line)
244
+ cleared?.(r.line);
245
+ index = -1;
246
+ browsing = false;
247
+ edit('', 0);
248
+ }
249
+ // When the last key was Enter, for the \n of a \r\n pair.
250
+ let returnAt = 0;
251
+ // Pasted text put in the buffer but not yet drawn.
252
+ let undrawn = false;
253
+ r._ttyWrite = (s, key) => {
254
+ const name = key?.name;
255
+ if (name === 'enter' && returnAt && Date.now() - returnAt <= r.crlfDelay) {
256
+ returnAt = 0;
257
+ return;
258
+ }
259
+ returnAt = name === 'return' ? Date.now() : 0;
260
+ const enter = name === 'return' || name === 'enter';
261
+ // Shift is let through: readline's own Shift-Up would browse the
262
+ // history its way.
263
+ const plain = !key?.ctrl && !key?.meta;
264
+ const arrow = (name === 'up' || name === 'down') && plain;
265
+ const historyKey = arrow || (key?.ctrl && !key.meta && (name === 'p' || name === 'n'));
266
+ if (!historyKey) {
267
+ browsing = false;
268
+ index = -1;
269
+ }
270
+ // readline turns `isCompletionEnabled` off for all but the last key
271
+ // of pasted text, and then writes each character straight out with
272
+ // no redraw, even when the line wraps, so its record of the rows
273
+ // drawn (`prevRows`) goes stale. So pasted text, and the newlines
274
+ // within a block, go in the buffer here, drawn once at the end.
275
+ const typed = typeof s === 'string' && s !== '' && !/[\x00-\x08\x0a-\x1f\x7f]/.test(s) && plain;
276
+ if (!r.isCompletionEnabled && (typed || (enter && plain && !complete(r.line)))) {
277
+ if (!undrawn)
278
+ beforeEdit(r.line, r.cursor);
279
+ const text = enter ? '\n' : s;
280
+ r.line = r.line.slice(0, r.cursor) + text + r.line.slice(r.cursor);
281
+ r.cursor += text.length;
282
+ undrawn = true;
283
+ return;
284
+ }
285
+ if (undrawn) {
286
+ undrawn = false;
287
+ r._refreshLine();
288
+ }
289
+ if (enter) {
290
+ if (key?.meta || !complete(r.line)) {
291
+ insert('\n');
292
+ return;
293
+ }
294
+ // Drawn whole, cursor at the end, so the terminal's scrollback
295
+ // keeps all of what was sent.
296
+ whole = true;
297
+ r.cursor = r.line.length;
298
+ r._refreshLine();
299
+ ttyWrite(s, key);
300
+ whole = false;
301
+ top = 0;
302
+ return;
303
+ }
304
+ if (arrow) {
305
+ if (browsing || !moveLine(name === 'up'))
306
+ browse(name === 'up');
307
+ return;
308
+ }
309
+ if (historyKey) {
310
+ browse(name === 'p');
311
+ return;
312
+ }
313
+ if (name === 'escape') {
314
+ clear();
315
+ return;
316
+ }
317
+ if ((name === 'home' && plain) || (key?.ctrl && name === 'a')) {
318
+ set(r.line, lineStart());
319
+ return;
320
+ }
321
+ if ((name === 'end' && plain) || (key?.ctrl && name === 'e')) {
322
+ set(r.line, lineEnd());
323
+ return;
324
+ }
325
+ if (key?.ctrl && name === 'u') {
326
+ const start = lineStart();
327
+ edit(r.line.slice(0, start) + r.line.slice(r.cursor), start, r.line.slice(start, r.cursor));
328
+ return;
329
+ }
330
+ // At a line's end, Ctrl-K joins the next line onto it, as in Emacs.
331
+ if (key?.ctrl && name === 'k') {
332
+ const end = r.cursor === lineEnd() && r.cursor < r.line.length ? r.cursor + 1 : lineEnd();
333
+ edit(r.line.slice(0, r.cursor) + r.line.slice(end), r.cursor, r.line.slice(r.cursor, end));
334
+ return;
335
+ }
336
+ // Typed at the buffer's end, readline writes the text straight out
337
+ // with no redraw, so in color it is redrawn to take the typed color.
338
+ const atEnd = typed && r.cursor === r.line.length;
339
+ ttyWrite(s, key);
340
+ if (paint && atEnd)
341
+ r._refreshLine();
342
+ };
343
+ return { clear };
344
+ }
345
+ // A readline method kept under a symbol, found by its name, or one that
346
+ // does nothing should a Node version not have it.
347
+ function internal(rl, name) {
348
+ for (let proto = Object.getPrototypeOf(rl); proto; proto = Object.getPrototypeOf(proto)) {
349
+ const symbol = Object.getOwnPropertySymbols(proto).find((s) => s.description === name);
350
+ if (symbol)
351
+ return (...args) => rl[symbol](...args);
352
+ }
353
+ return () => undefined;
354
+ }
@@ -0,0 +1,13 @@
1
+ /** How many entries a session loads, and the file keeps. */
2
+ export declare const HISTORY_SIZE = 1000;
3
+ /** The history file, or null when history isn't kept. */
4
+ export declare function historyFile(): string | null;
5
+ /**
6
+ * The entries in the file's last `HISTORY_SIZE` lines, newest first, as
7
+ * readline takes them. A missing or unreadable file is no history.
8
+ * The file is cut back to that many lines once it holds twice as many,
9
+ * since each session only appends to it.
10
+ */
11
+ export declare function loadHistory(file: string): string[];
12
+ /** Adds an entry to the end of the file. Throws when it can't. */
13
+ export declare function appendHistory(file: string, entry: string): void;
@@ -0,0 +1,89 @@
1
+ // The `--interactive` prompt's history, kept across sessions in
2
+ // ~/.sm-dev-history (or `SM_DEV_HISTORY`; set it empty for none).
3
+ //
4
+ // As in a shell with `histappend`, sessions share the file without
5
+ // overwriting each other: each loads it when it starts, and appends
6
+ // its own commands as they are typed. A session doesn't see another's
7
+ // commands until it is restarted.
8
+ //
9
+ // One entry per line, as a JSON string, since a `{{` block is one
10
+ // entry of several lines.
11
+ import fs from 'node:fs';
12
+ import os from 'node:os';
13
+ import path from 'node:path';
14
+ /** How many entries a session loads, and the file keeps. */
15
+ export const HISTORY_SIZE = 1000;
16
+ /** The history file, or null when history isn't kept. */
17
+ export function historyFile() {
18
+ const chosen = process.env.SM_DEV_HISTORY;
19
+ if (chosen !== undefined)
20
+ return chosen.trim() ? path.resolve(chosen.trim()) : null;
21
+ return path.join(os.homedir(), '.sm-dev-history');
22
+ }
23
+ /**
24
+ * The entries in the file's last `HISTORY_SIZE` lines, newest first, as
25
+ * readline takes them. A missing or unreadable file is no history.
26
+ * The file is cut back to that many lines once it holds twice as many,
27
+ * since each session only appends to it.
28
+ */
29
+ export function loadHistory(file) {
30
+ let text;
31
+ try {
32
+ text = fs.readFileSync(file, 'utf8');
33
+ }
34
+ catch {
35
+ return [];
36
+ }
37
+ const lines = text.split('\n').filter(Boolean);
38
+ const entries = [];
39
+ for (const line of lines.slice(-HISTORY_SIZE)) {
40
+ try {
41
+ const entry = JSON.parse(line);
42
+ if (typeof entry === 'string' && entry !== entries.at(-1))
43
+ entries.push(entry);
44
+ }
45
+ catch {
46
+ // A line cut short by a crash mid-write: skipped.
47
+ }
48
+ }
49
+ try {
50
+ if (lines.length >= 2 * HISTORY_SIZE) {
51
+ // Written aside and renamed, so a session reading the file never
52
+ // sees half of it. One appending meanwhile loses that entry.
53
+ const temp = `${file}.${process.pid}.tmp`;
54
+ try {
55
+ writePrivate(temp, lines.slice(-HISTORY_SIZE).join('\n') + '\n', 'w');
56
+ fs.renameSync(temp, file);
57
+ }
58
+ finally {
59
+ fs.rmSync(temp, { force: true });
60
+ }
61
+ }
62
+ else if (text && !text.endsWith('\n')) {
63
+ // End a cut-short line, so the next entry doesn't join it.
64
+ writePrivate(file, '\n', 'a');
65
+ }
66
+ }
67
+ catch {
68
+ // Tidying only. A file that can't be written is reported by the
69
+ // first appendHistory.
70
+ }
71
+ return entries.reverse();
72
+ }
73
+ /** Adds an entry to the end of the file. Throws when it can't. */
74
+ export function appendHistory(file, entry) {
75
+ writePrivate(file, JSON.stringify(entry) + '\n', 'a');
76
+ }
77
+ // Every write of the file goes through here, so it stays readable only
78
+ // by its owner: commands can hold passwords (`type`) and tokens. A
79
+ // file's mode is only set when it is made, so it is set again here.
80
+ function writePrivate(file, text, flag) {
81
+ const fd = fs.openSync(file, flag, 0o600);
82
+ try {
83
+ fs.fchmodSync(fd, 0o600);
84
+ fs.writeSync(fd, text);
85
+ }
86
+ finally {
87
+ fs.closeSync(fd);
88
+ }
89
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * How to rebuild what this sm-dev runs from, as a sentence: in the
3
+ * checkout itself, or in the checkout an installed copy came from and
4
+ * then here.
5
+ */
6
+ export declare function rebuildAdvice(): string;
@@ -0,0 +1,39 @@
1
+ // How to bring this sm-dev up to date, for messages that need it.
2
+ //
3
+ // The build records the checkout it was built from in
4
+ // lib/build-info.json. That covers both ways sm-dev runs:
5
+ // - In the checkout itself (scripts/sm-dev), from its own lib/.
6
+ // - In a repo of userscripts, from the copy `pnpm install` took (with
7
+ // no src/ of its own). `pnpm install` copies the checkout's lib/ as
8
+ // it is, without building it first, so the fix needs a build there
9
+ // too.
10
+ import { readFileSync } from 'node:fs';
11
+ import path from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { quoted } from './output-style.js';
14
+ // lib/, beside this module's lib/harness/.
15
+ const LIB_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
16
+ /**
17
+ * How to rebuild what this sm-dev runs from, as a sentence: in the
18
+ * checkout itself, or in the checkout an installed copy came from and
19
+ * then here.
20
+ */
21
+ export function rebuildAdvice() {
22
+ const source = buildSource();
23
+ if (!source)
24
+ return 'Run `pnpm run build` in SourceMonkey, and `pnpm install` again where it is installed.';
25
+ if (path.resolve(source, 'lib') === LIB_DIR)
26
+ return 'Run `pnpm run build`.';
27
+ return `Run ${quoted('pnpm run build')} in ${source}, then ${quoted('pnpm install')} here.`;
28
+ }
29
+ // The checkout lib/ was built from, or null: built before
30
+ // build-info.json existed, or not by build.mjs, leaves nothing to go by.
31
+ function buildSource() {
32
+ try {
33
+ const info = JSON.parse(readFileSync(path.join(LIB_DIR, 'build-info.json'), 'utf8'));
34
+ return typeof info.source === 'string' ? info.source : null;
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
@@ -0,0 +1,47 @@
1
+ import { type FileHandle } from 'node:fs/promises';
2
+ /**
3
+ * Where the logs go: `/tmp/sm-dev-logs-<user>` on most machines.
4
+ * `SM_DEV_LOGS_DIR` moves it, for tests.
5
+ */
6
+ export declare function runLogsDir(): string;
7
+ /**
8
+ * Where a temp browser run's downloads go: in the logs folder, named
9
+ * after the run's log when it has one, and kept after the run ends as
10
+ * the log is (the browser's own profile is deleted).
11
+ */
12
+ export declare function runDownloadsDir(log?: string): string;
13
+ /**
14
+ * Local time as `20260408-203012-345`: SeeWhatISee's file-name
15
+ * timestamp. Names the log files, and starts each line in them.
16
+ */
17
+ export declare function logTimestamp(ms?: number): string;
18
+ /** A log line's timestamp and the space after it, for taking back off. */
19
+ export declare const LOG_LINE_PREFIX: RegExp;
20
+ /**
21
+ * Creates a new log file, `sm-dev-<timestamp>.log`. A name already
22
+ * taken moves the timestamp on a millisecond and tries again; created
23
+ * exclusively, so two runs starting together can't share one.
24
+ */
25
+ export declare function createRunLog(): Promise<{
26
+ file: string;
27
+ handle: FileHandle;
28
+ }>;
29
+ /**
30
+ * Starts each line this process writes to stdout and stderr with the
31
+ * time it was written. For the detached run, whose output is its log
32
+ * file. Output that bypasses the streams (a crash in Node itself)
33
+ * still lands in the file, unstamped.
34
+ */
35
+ export declare function timestampOutputLines(): void;
36
+ /**
37
+ * A log's full path, and its link's name when it has one, for printing:
38
+ * `/tmp/sm-dev-logs-you/sm-dev-20260926-063756-629.log (D1.log)`.
39
+ */
40
+ export declare function runLogLabel(file: string, id: string | undefined): string;
41
+ /**
42
+ * Points `<run id>.log`, beside the log, at it: the latest log of
43
+ * a run with that id, found by the id alone. Swapped in by a rename, so
44
+ * the link is never missing or half made. A failure only costs the
45
+ * link, so it is said and the run goes on.
46
+ */
47
+ export declare function linkRunLog(file: string, id: string, out: (line: string) => void): Promise<void>;