@wenathlan/devthink 2.0.13 → 2.0.15

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 (444) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/accounts.ts +2087 -0
  3. package/agent.ts +2331 -0
  4. package/antigravity.ts +2139 -0
  5. package/apifreeze.ts +4146 -0
  6. package/auth.ts +642 -0
  7. package/background.ts +16837 -0
  8. package/bridge.ts +2080 -0
  9. package/build.ts +54 -0
  10. package/bun.ts +26 -0
  11. package/capture.ts +1614 -0
  12. package/checksecrets.ts +72 -0
  13. package/cjs.ts +9 -0
  14. package/cli.ts +3500 -0
  15. package/commands.ts +1101 -0
  16. package/companion.ts +450 -0
  17. package/compatibility.ts +37 -0
  18. package/config.ts +3137 -0
  19. package/constants.ts +943 -0
  20. package/core.ts +1473 -0
  21. package/crossbrowser.ts +1359 -0
  22. package/dashdone.ts +605 -0
  23. package/data.ts +943 -0
  24. package/database.ts +133 -0
  25. package/debug.ts +850 -0
  26. package/deno.ts +27 -0
  27. package/devthink.ts +707 -0
  28. package/dist/accounts.d.ts +409 -0
  29. package/dist/agent.d.ts +837 -0
  30. package/dist/antigravity.d.ts +380 -0
  31. package/dist/apifreeze.d.ts +113 -0
  32. package/dist/auth.d.ts +224 -0
  33. package/{bridge.d.ts → dist/bridge.d.ts} +1 -1
  34. package/dist/capture.d.ts +595 -0
  35. package/dist/cjs.d.ts +6 -0
  36. package/{cli.js → dist/cli.js} +2 -2
  37. package/dist/commands.d.ts +310 -0
  38. package/dist/compatibility.d.ts +17 -0
  39. package/dist/config.d.ts +1853 -0
  40. package/dist/constants.d.ts +637 -0
  41. package/dist/core.d.ts +361 -0
  42. package/dist/data.d.ts +330 -0
  43. package/dist/database.d.ts +24 -0
  44. package/dist/debug.d.ts +270 -0
  45. package/dist/engine.d.ts +74 -0
  46. package/dist/environments.d.ts +244 -0
  47. package/dist/evidence.d.ts +277 -0
  48. package/dist/export.d.ts +311 -0
  49. package/dist/fingerprint.d.ts +594 -0
  50. package/dist/flow.d.ts +300 -0
  51. package/dist/gates.d.ts +103 -0
  52. package/dist/identity.d.ts +51 -0
  53. package/dist/ids.d.ts +7 -0
  54. package/dist/llm.d.ts +652 -0
  55. package/dist/models.d.ts +1807 -0
  56. package/dist/net.d.ts +325 -0
  57. package/dist/neutral.d.ts +41 -0
  58. package/dist/oauth.d.ts +703 -0
  59. package/dist/page.d.ts +1166 -0
  60. package/dist/perf.d.ts +481 -0
  61. package/dist/plan.d.ts +134 -0
  62. package/dist/project.d.ts +395 -0
  63. package/dist/providers.d.ts +37 -0
  64. package/dist/quota.d.ts +453 -0
  65. package/dist/recovery.d.ts +621 -0
  66. package/dist/request.d.ts +1492 -0
  67. package/dist/run.d.ts +779 -0
  68. package/dist/runtime.d.ts +212 -0
  69. package/dist/search.d.ts +375 -0
  70. package/dist/security.d.ts +534 -0
  71. package/dist/serve.d.ts +136 -0
  72. package/dist/session.d.ts +296 -0
  73. package/dist/storage.d.ts +53 -0
  74. package/dist/streaming.d.ts +1119 -0
  75. package/dist/swarm.d.ts +328 -0
  76. package/dist/tools.d.ts +170 -0
  77. package/dist/types.d.ts +8357 -0
  78. package/dist/utils.d.ts +82 -0
  79. package/dist/version.d.ts +7 -0
  80. package/dist/versionregistry.d.ts +406 -0
  81. package/dist/views.d.ts +579 -0
  82. package/dist/workbenchsession.d.ts +69 -0
  83. package/dist/workflow.d.ts +649 -0
  84. package/docs/readme.md +92 -0
  85. package/docs/schemas/antigravity.json +192 -0
  86. package/engine.ts +1524 -0
  87. package/environments.ts +1005 -0
  88. package/evidence.ts +643 -0
  89. package/export.ts +889 -0
  90. package/fingerprint.ts +1458 -0
  91. package/flow.ts +1190 -0
  92. package/gates.ts +307 -0
  93. package/gateway.ts +34 -0
  94. package/hardening.ts +191 -0
  95. package/headless.ts +369 -0
  96. package/http.ts +2110 -0
  97. package/identity.ts +246 -0
  98. package/ids.ts +26 -0
  99. package/index.ts +369 -0
  100. package/llm.ts +2682 -0
  101. package/mcp.ts +1835 -0
  102. package/{memory.js → memory.ts} +3834 -2310
  103. package/models.ts +3805 -0
  104. package/modes.ts +43 -0
  105. package/net.ts +1214 -0
  106. package/neutral.ts +362 -0
  107. package/node.ts +26 -0
  108. package/oauth.ts +2216 -0
  109. package/offscreen.ts +170 -0
  110. package/pack.ts +956 -0
  111. package/package.json +175 -82
  112. package/page.ts +7824 -0
  113. package/pagebridge.ts +1116 -0
  114. package/perf.ts +957 -0
  115. package/plan.ts +672 -0
  116. package/plugin.ts +76 -0
  117. package/{policy.js → policy.ts} +5073 -4679
  118. package/prisma.config.ts +27 -0
  119. package/progress.ts +1308 -0
  120. package/project.ts +1291 -0
  121. package/protocol.ts +4187 -0
  122. package/providers.ts +246 -0
  123. package/quota.ts +1887 -0
  124. package/recovery.ts +2433 -0
  125. package/request.ts +4482 -0
  126. package/run.ts +2135 -0
  127. package/runtime.ts +526 -0
  128. package/search.ts +1760 -0
  129. package/security.ts +1673 -0
  130. package/serve.ts +419 -0
  131. package/server.ts +5538 -0
  132. package/session.ts +1162 -0
  133. package/storage.ts +182 -0
  134. package/streaming.ts +4101 -0
  135. package/swarm.ts +884 -0
  136. package/sync.ts +56 -0
  137. package/tests/agentcert.test.ts +128 -0
  138. package/tests/agentfleet.test.ts +214 -0
  139. package/tests/agentreview.test.ts +241 -0
  140. package/tests/agentstream.test.ts +335 -0
  141. package/tests/agentwork.test.ts +356 -0
  142. package/tests/apifreeze.test.ts +290 -0
  143. package/tests/apimap.test.ts +112 -0
  144. package/tests/approvalgate.test.ts +175 -0
  145. package/tests/artifactmanifest.test.ts +204 -0
  146. package/tests/attentionfeed.test.ts +163 -0
  147. package/tests/backgroundruns.test.ts +100 -0
  148. package/tests/batchscheduling.test.ts +131 -0
  149. package/tests/blackboard.test.ts +238 -0
  150. package/tests/bridge.test.ts +1088 -0
  151. package/tests/browsercoverage.test.ts +134 -0
  152. package/tests/browserpolyfills.test.ts +124 -0
  153. package/tests/build.mjs +1260 -0
  154. package/tests/clientauth.test.ts +223 -0
  155. package/tests/clitools.test.ts +578 -0
  156. package/tests/confirmgates.test.ts +190 -0
  157. package/tests/containerpack.test.ts +143 -0
  158. package/tests/controlflow.test.ts +2120 -0
  159. package/tests/coordination.test.ts +345 -0
  160. package/tests/copyscreen.test.ts +139 -0
  161. package/tests/core.test.ts +417 -0
  162. package/tests/costcert.test.ts +95 -0
  163. package/tests/crossbrowser.test.ts +115 -0
  164. package/tests/cspaudit.test.ts +79 -0
  165. package/tests/dashdone.test.ts +615 -0
  166. package/tests/datagrid.test.ts +137 -0
  167. package/tests/doccheck.test.ts +107 -0
  168. package/tests/dombatch.test.ts +109 -0
  169. package/tests/emulation.test.ts +282 -0
  170. package/tests/environments.test.ts +259 -0
  171. package/tests/evidenceviews.test.ts +88 -0
  172. package/tests/exporttools.test.ts +185 -0
  173. package/tests/firefoxprep.test.ts +134 -0
  174. package/tests/flowlibrary.test.ts +242 -0
  175. package/tests/flowrun.test.ts +303 -0
  176. package/tests/forensics.test.ts +439 -0
  177. package/tests/gateway.test.ts +939 -0
  178. package/tests/headlesslib.test.ts +171 -0
  179. package/tests/httpstream.test.ts +277 -0
  180. package/tests/immutablelog.test.ts +157 -0
  181. package/tests/inboundguard.test.ts +177 -0
  182. package/tests/lazyload.test.ts +72 -0
  183. package/tests/librarymodes.test.ts +623 -0
  184. package/tests/llm.test.ts +696 -0
  185. package/tests/manifest.test.ts +226 -0
  186. package/tests/maskinputs.test.ts +150 -0
  187. package/tests/matrixverify.test.ts +111 -0
  188. package/tests/mavenpack.test.ts +75 -0
  189. package/tests/mcpserver.test.ts +704 -0
  190. package/tests/memory.test.ts +4155 -0
  191. package/tests/memorycare.test.ts +230 -0
  192. package/tests/migrateplan.test.ts +349 -0
  193. package/tests/minimization.test.ts +414 -0
  194. package/tests/modelroute.test.ts +112 -0
  195. package/tests/multiagent.test.ts +440 -0
  196. package/tests/nativebridge.test.ts +807 -0
  197. package/tests/navigation.test.ts +284 -0
  198. package/tests/nugetpack.test.ts +83 -0
  199. package/tests/orchestration.test.ts +449 -0
  200. package/tests/originpolicy.test.ts +405 -0
  201. package/tests/outputcompare.test.ts +97 -0
  202. package/tests/package.test.ts +91 -0
  203. package/tests/pagebridge.test.ts +5909 -0
  204. package/tests/pentest.test.ts +75 -0
  205. package/tests/perfrecords.test.ts +257 -0
  206. package/tests/permdiff.test.ts +85 -0
  207. package/tests/phishguard.test.ts +128 -0
  208. package/tests/pickerviews.test.ts +155 -0
  209. package/tests/pipelines.test.ts +381 -0
  210. package/tests/planlint.test.ts +240 -0
  211. package/tests/planreview.test.ts +383 -0
  212. package/tests/platformtargets.test.ts +56 -0
  213. package/tests/policy.test.ts +8284 -0
  214. package/tests/portability.test.ts +96 -0
  215. package/tests/progress.test.ts +1036 -0
  216. package/tests/promptlibrary.test.ts +116 -0
  217. package/tests/protocol.test.ts +4793 -0
  218. package/tests/publishingpipeline.test.ts +314 -0
  219. package/tests/quickactions.test.ts +164 -0
  220. package/tests/readiness.test.ts +78 -0
  221. package/tests/recipes.test.ts +163 -0
  222. package/tests/redactshots.test.ts +216 -0
  223. package/tests/registernvidiakeys.ts +51 -0
  224. package/tests/relayserve.test.ts +304 -0
  225. package/tests/releasenotes.test.ts +276 -0
  226. package/tests/resilience.test.ts +467 -0
  227. package/tests/resourceaware.test.ts +187 -0
  228. package/tests/runbudget.test.ts +125 -0
  229. package/tests/runhistory.test.ts +299 -0
  230. package/tests/runreplay.test.ts +113 -0
  231. package/tests/runresume.test.ts +184 -0
  232. package/tests/runstate.test.ts +288 -0
  233. package/tests/runtimeadapters.test.ts +251 -0
  234. package/tests/safariskeleton.test.ts +115 -0
  235. package/tests/sandboxframe.test.ts +196 -0
  236. package/tests/sbom.test.ts +105 -0
  237. package/tests/secretvault.test.ts +188 -0
  238. package/tests/selcache.test.ts +111 -0
  239. package/tests/sessioninterface.test.ts +791 -0
  240. package/tests/sessions.test.ts +591 -0
  241. package/tests/siteprefs.test.ts +228 -0
  242. package/tests/snapshotdelta.test.ts +165 -0
  243. package/tests/statusviews.test.ts +237 -0
  244. package/tests/stepmeter.test.ts +200 -0
  245. package/tests/streamparse.test.ts +163 -0
  246. package/tests/stylepruning.test.ts +90 -0
  247. package/tests/surfaces.test.ts +282 -0
  248. package/tests/sweep.test.ts +78 -0
  249. package/tests/syncbridge.test.ts +164 -0
  250. package/tests/taskqueue.test.ts +206 -0
  251. package/tests/toolcalls.test.ts +336 -0
  252. package/tests/toolcatalog.test.ts +201 -0
  253. package/tests/torture-agent.test.ts +706 -0
  254. package/tests/torture-auth.test.ts +461 -0
  255. package/tests/torture-commands.test.ts +895 -0
  256. package/tests/torture-data.test.ts +923 -0
  257. package/tests/torture-environments.test.ts +285 -0
  258. package/tests/torture-export.test.ts +263 -0
  259. package/tests/torture-http.test.ts +1379 -0
  260. package/tests/torture-llm.test.ts +808 -0
  261. package/tests/torture-memory.test.ts +1084 -0
  262. package/tests/torture-plan.test.ts +829 -0
  263. package/tests/torture-run.test.ts +626 -0
  264. package/tests/torture-session.test.ts +928 -0
  265. package/tests/torture-swarm.test.ts +1245 -0
  266. package/tests/torture-tools.test.ts +964 -0
  267. package/tests/torture-workflow.test.ts +2354 -0
  268. package/tests/tourviews.test.ts +52 -0
  269. package/tests/transparency.test.ts +147 -0
  270. package/tests/trigger.test.ts +941 -0
  271. package/tests/v1sunset.test.ts +428 -0
  272. package/tests/virtlist.test.ts +94 -0
  273. package/tests/vision.test.ts +540 -0
  274. package/tests/vsixpack.test.ts +145 -0
  275. package/tests/webapi.test.ts +523 -0
  276. package/tests/workflow.test.ts +1785 -0
  277. package/tests/workfloweditor.test.ts +759 -0
  278. package/tests/xpipack.test.ts +128 -0
  279. package/tools.ts +734 -0
  280. package/types.ts +9908 -0
  281. package/umd.ts +9 -0
  282. package/utils.ts +249 -0
  283. package/version.ts +8 -0
  284. package/versionregistry.ts +1310 -0
  285. package/views.ts +2153 -0
  286. package/vitest.config.ts +12 -0
  287. package/web/App.tsx +78 -0
  288. package/web/README.md +47 -0
  289. package/web/capacitor.config.ts +46 -0
  290. package/web/const.ts +19 -0
  291. package/web/control.shell.tsx +66 -0
  292. package/web/dashboardpage.ts +554 -0
  293. package/web/db.ts +493 -0
  294. package/web/design.html +541 -0
  295. package/web/gatewayview/Gateway.tsx +345 -0
  296. package/web/gatewayview/config.ts +1042 -0
  297. package/web/gatewayview/definition.ts +174 -0
  298. package/web/icons.ts +15 -0
  299. package/{manifest.json → web/manifest.json} +1 -1
  300. package/web/optionspage.ts +691 -0
  301. package/web/popup.ts +1651 -0
  302. package/web/schema.prisma +176 -0
  303. package/web/sidepanel.ts +7941 -0
  304. package/web/sitemanifest.json +13 -0
  305. package/web/terminal-ui.tsx +622 -0
  306. package/web/transparencypage.ts +541 -0
  307. package/web/vite.config.ts +39 -0
  308. package/web/workspace.ts +23 -0
  309. package/workbenchmemory.ts +104 -0
  310. package/workbenchsession.ts +304 -0
  311. package/workflow.ts +4495 -0
  312. package/bridge.d.ts.map +0 -1
  313. package/bridge.js +0 -1877
  314. package/bun.d.ts.map +0 -1
  315. package/bun.js +0 -52329
  316. package/caps/background.json +0 -712
  317. package/caps/cli.json +0 -360
  318. package/caps/library.json +0 -3611
  319. package/caps/mcp.json +0 -126
  320. package/caps/pagebridge.json +0 -377
  321. package/caps/popup.json +0 -51
  322. package/caps/sidepanel.json +0 -522
  323. package/checksums.txt +0 -142
  324. package/cli.d.ts.map +0 -1
  325. package/companion.d.ts.map +0 -1
  326. package/companion.js +0 -389
  327. package/crossbrowser.d.ts.map +0 -1
  328. package/crossbrowser.js +0 -1191
  329. package/dashdone.d.ts.map +0 -1
  330. package/dashdone.js +0 -368
  331. package/deno.d.ts.map +0 -1
  332. package/deno.js +0 -52325
  333. package/devthink.umd.js +0 -52346
  334. package/fixtures/bun.ts +0 -7
  335. package/fixtures/cjs.js +0 -6
  336. package/fixtures/deno.ts +0 -6
  337. package/fixtures/example-org-pagestate.json +0 -22
  338. package/fixtures/importers/automa-workflow.json +0 -18
  339. package/fixtures/importers/selenium-side.json +0 -20
  340. package/fixtures/importers/tabular-plan.csv +0 -7
  341. package/fixtures/importers/uivision-macro.json +0 -10
  342. package/fixtures/importers/v1-plan.json +0 -13
  343. package/fixtures/mcp-client.mjs +0 -14
  344. package/fixtures/pages/checkout.html +0 -192
  345. package/fixtures/pages/dashboard.html +0 -110
  346. package/fixtures/pages/feed.html +0 -113
  347. package/fixtures/pages/product-grid.html +0 -150
  348. package/fixtures/plans/form-inventory-plan.json +0 -11
  349. package/fixtures/plans/release-notes-plan.json +0 -11
  350. package/fixtures/recipes/agentscompete.json +0 -11
  351. package/fixtures/recipes/agentsforms.json +0 -11
  352. package/fixtures/recipes/agentsmonitor.json +0 -11
  353. package/fixtures/recipes/agentsreview.json +0 -29
  354. package/fixtures/recipes/agentswarm.json +0 -11
  355. package/fixtures/recipes/fillcalendar.json +0 -12
  356. package/fixtures/recipes/fillcheckout.json +0 -13
  357. package/fixtures/recipes/filldependentdropdowns.json +0 -12
  358. package/fixtures/recipes/fillfromcsv.json +0 -13
  359. package/fixtures/recipes/fillloginform.json +0 -12
  360. package/fixtures/recipes/fillmultistepwizard.json +0 -13
  361. package/fixtures/recipes/fillregistration.json +0 -12
  362. package/fixtures/recipes/fillsearchfilters.json +0 -12
  363. package/fixtures/recipes/filltypeahead.json +0 -11
  364. package/fixtures/recipes/monitoralerts.json +0 -12
  365. package/fixtures/recipes/monitoravailability.json +0 -11
  366. package/fixtures/recipes/monitornetworkquiet.json +0 -12
  367. package/fixtures/recipes/monitorpagechanges.json +0 -11
  368. package/fixtures/recipes/monitorpricechange.json +0 -11
  369. package/fixtures/recipes/monitorschedule.json +0 -12
  370. package/fixtures/recipes/scrapedynamicprices.json +0 -13
  371. package/fixtures/recipes/scrapeimagegallery.json +0 -12
  372. package/fixtures/recipes/scrapeinfinitefeed.json +0 -13
  373. package/fixtures/recipes/scrapenestedlists.json +0 -12
  374. package/fixtures/recipes/scrapeproductgrid.json +0 -12
  375. package/fixtures/recipes/scrapereviews.json +0 -12
  376. package/fixtures/recipes/scrapesearchresults.json +0 -12
  377. package/fixtures/recipes/scrapetablepagination.json +0 -12
  378. package/fixtures/recipes/testaccessibility.json +0 -34
  379. package/fixtures/recipes/testconsoleerrors.json +0 -12
  380. package/fixtures/recipes/testdeeplinks.json +0 -12
  381. package/fixtures/recipes/testformvalidation.json +0 -13
  382. package/fixtures/recipes/testlayoutshift.json +0 -12
  383. package/fixtures/recipes/testlinkcheck.json +0 -12
  384. package/fixtures/recipes/testloadtiming.json +0 -12
  385. package/fixtures/recipes/testvisualdiff.json +0 -12
  386. package/fixtures/workflows/changelog-digest-workflow.json +0 -13
  387. package/fixtures/workflows/form-survey-workflow.json +0 -13
  388. package/gallery.json +0 -706
  389. package/hardening.d.ts.map +0 -1
  390. package/hardening.js +0 -188
  391. package/headless.d.ts.map +0 -1
  392. package/headless.js +0 -11522
  393. package/http.d.ts.map +0 -1
  394. package/http.js +0 -2165
  395. package/index.cjs +0 -54682
  396. package/index.d.ts.map +0 -1
  397. package/index.js +0 -52310
  398. package/index.neutral.js +0 -46343
  399. package/mcp.d.ts.map +0 -1
  400. package/mcp.js +0 -20515
  401. package/memory.d.ts.map +0 -1
  402. package/nativehost.template.json +0 -10
  403. package/node.cjs +0 -54686
  404. package/node.d.ts.map +0 -1
  405. package/pack.d.ts.map +0 -1
  406. package/pack.js +0 -805
  407. package/policy.d.ts.map +0 -1
  408. package/progress.d.ts.map +0 -1
  409. package/progress.js +0 -645
  410. package/protocol.d.ts.map +0 -1
  411. package/protocol.js +0 -8334
  412. package/schemas/audit.schema.json +0 -226
  413. package/schemas/capability.schema.json +0 -113
  414. package/schemas/envelope.schema.json +0 -162
  415. package/schemas/memory.schema.json +0 -51
  416. package/schemas/observation.schema.json +0 -113
  417. package/schemas/plan.schema.json +0 -208
  418. package/schemas/progress.schema.json +0 -126
  419. package/schemas/proposal.schema.json +0 -68
  420. package/schemas/session.schema.json +0 -82
  421. package/schemas/tool.schema.json +0 -94
  422. package/server.d.ts.map +0 -1
  423. package/server.js +0 -28877
  424. package/umd-example.html +0 -18
  425. package/umd.d.ts.map +0 -1
  426. /package/{bun.d.ts → dist/bun.d.ts} +0 -0
  427. /package/{cli.d.ts → dist/cli.d.ts} +0 -0
  428. /package/{companion.d.ts → dist/companion.d.ts} +0 -0
  429. /package/{crossbrowser.d.ts → dist/crossbrowser.d.ts} +0 -0
  430. /package/{dashdone.d.ts → dist/dashdone.d.ts} +0 -0
  431. /package/{deno.d.ts → dist/deno.d.ts} +0 -0
  432. /package/{hardening.d.ts → dist/hardening.d.ts} +0 -0
  433. /package/{headless.d.ts → dist/headless.d.ts} +0 -0
  434. /package/{http.d.ts → dist/http.d.ts} +0 -0
  435. /package/{index.d.ts → dist/index.d.ts} +0 -0
  436. /package/{mcp.d.ts → dist/mcp.d.ts} +0 -0
  437. /package/{memory.d.ts → dist/memory.d.ts} +0 -0
  438. /package/{node.d.ts → dist/node.d.ts} +0 -0
  439. /package/{pack.d.ts → dist/pack.d.ts} +0 -0
  440. /package/{policy.d.ts → dist/policy.d.ts} +0 -0
  441. /package/{progress.d.ts → dist/progress.d.ts} +0 -0
  442. /package/{protocol.d.ts → dist/protocol.d.ts} +0 -0
  443. /package/{server.d.ts → dist/server.d.ts} +0 -0
  444. /package/{umd.d.ts → dist/umd.d.ts} +0 -0
@@ -0,0 +1,226 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { createHash } from "node:crypto";
4
+ import { describe, expect, it } from "vitest";
5
+
6
+ describe("extension manifest", () => {
7
+ it("keeps privileged browser capabilities optional and out of the default package", async () => {
8
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as {
9
+ manifest_version: number;
10
+ version: string;
11
+ key?: string;
12
+ permissions: string[];
13
+ optional_permissions?: string[];
14
+ host_permissions?: string[];
15
+ content_scripts?: unknown[];
16
+ sandbox?: { pages?: string[] };
17
+ offscreen?: { document?: string; reasons?: string[]; justification?: string };
18
+ };
19
+ const packagejson = JSON.parse(await readFile("package.json", "utf8")) as { version: string };
20
+ expect(manifest.manifest_version).toBe(3);
21
+ expect(manifest.version).toBe(packagejson.version);
22
+ expect(manifest.permissions).toEqual(["activeTab", "storage", "scripting", "sidePanel"]);
23
+ expect(manifest.optional_permissions ?? []).toEqual([
24
+ "tabs",
25
+ "downloads",
26
+ "clipboardRead",
27
+ "clipboardWrite",
28
+ "offscreen",
29
+ "nativeMessaging",
30
+ ]);
31
+ expect(manifest.host_permissions ?? []).toEqual([]);
32
+ expect(manifest.content_scripts ?? []).toEqual([]);
33
+ expect(manifest.sandbox).toEqual({ pages: ["sandbox.html"] });
34
+ expect(manifest.offscreen).toMatchObject({ document: "offscreen.html", reasons: ["DOM_PARSER", "WORKERS"] });
35
+ expect((manifest.offscreen as { justification?: string }).justification ?? "").toMatch(
36
+ /offscreen document worker pool/,
37
+ );
38
+ });
39
+
40
+ it("keeps the native messaging permission optional only so the native bridge stays a per install user choice", async () => {
41
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as {
42
+ permissions: string[];
43
+ optional_permissions?: string[];
44
+ };
45
+ expect(manifest.permissions).not.toContain("nativeMessaging");
46
+ expect(manifest.optional_permissions ?? []).toContain("nativeMessaging");
47
+ });
48
+
49
+ it("keeps a stable, strictly decodable identity key", async () => {
50
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as { key?: string };
51
+ const key = manifest.key ?? "";
52
+ expect(key.length % 4).toBe(0);
53
+ expect(key).toMatch(/^[A-Za-z0-9+/]+={0,2}$/);
54
+ const der = Buffer.from(key, "base64");
55
+ expect(der.length).toBe(294);
56
+ expect(der.subarray(0, 2).equals(Buffer.from([0x30, 0x82]))).toBe(true);
57
+ expect(der.readUInt16BE(2)).toBe(der.length - 4);
58
+ const digest = createHash("sha256").update(der).digest("hex");
59
+ expect(digest).toMatch(/^[0-9a-f]{64}$/);
60
+ /* the identity pin rides the generated build artifact: the recorded digest never stays hard coded in a source file, the build writes it into dist/checksums.txt on every run and this pass verifies the manifest key against that artifact whenever the build ran */
61
+ if (existsSync("dist/checksums.txt")) {
62
+ const recorded = (await readFile("dist/checksums.txt", "utf8"))
63
+ .split("\n")
64
+ .find((line) => line.trim().endsWith(" manifest.json key"));
65
+ expect(recorded?.split(" ")[0]).toBe(digest);
66
+ }
67
+ });
68
+
69
+ it("declares the optionspage as the options surface and the dashboardpage chrome url without new permissions", async () => {
70
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as {
71
+ options_ui?: { page?: string; open_in_tab?: boolean };
72
+ chrome_url_overrides?: Record<string, string>;
73
+ };
74
+ expect(manifest.options_ui).toMatchObject({ page: "optionspage.html", open_in_tab: true });
75
+ expect(manifest.chrome_url_overrides).toEqual({ newtab: "dashboardpage.html" });
76
+ });
77
+
78
+ it("declares the icon family of the final polish at every required size with payloads that decode to the declared pixels", async () => {
79
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as {
80
+ icons?: Record<string, string>;
81
+ action?: { default_icon?: Record<string, string> };
82
+ };
83
+ const expected = {
84
+ "16": "icons/16.png",
85
+ "19": "icons/19.png",
86
+ "32": "icons/32.png",
87
+ "38": "icons/38.png",
88
+ "48": "icons/48.png",
89
+ "128": "icons/128.png",
90
+ };
91
+ expect(manifest.icons).toEqual(expected);
92
+ expect(manifest.action?.default_icon).toEqual(expected);
93
+ const iconssource = await readFile("web/icons.ts", "utf8");
94
+ const iconblock =
95
+ /export const iconpayloads: Record<string, string> = \{([\s\S]*?)\};/.exec(iconssource)?.[1] ?? "";
96
+ const iconentries = [...iconblock.matchAll(/"(\d+)": "([A-Za-z0-9+/=]+)"/g)].map((match) => ({
97
+ size: Number(match[1]),
98
+ base64: match[2] ?? "",
99
+ }));
100
+ expect(iconentries.map((entry) => entry.size).sort((a, b) => a - b)).toEqual([16, 19, 32, 38, 48, 128]);
101
+ const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
102
+ for (const entry of iconentries) {
103
+ const bytes = Buffer.from(entry.base64, "base64");
104
+ expect(bytes.subarray(0, 8).equals(signature)).toBe(true);
105
+ expect(bytes.readUInt32BE(16)).toBe(entry.size);
106
+ expect(bytes.readUInt32BE(20)).toBe(entry.size);
107
+ }
108
+ });
109
+ });
110
+
111
+ /* ── The frozen capability manifest declarations of the 1.1.91 api freeze. ── */
112
+ import { capmanifestof } from "../apifreeze.js";
113
+
114
+ describe("the frozen capability manifest declarations", () => {
115
+ it("matches the manifest permissions with the capmanifest declarations of every surface", async () => {
116
+ if (!existsSync("dist/caps"))
117
+ return; /* the capmanifest artifacts ride the build: the pass runs after pnpm build in the validate chain and the ci lanes */
118
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as {
119
+ version: string;
120
+ permissions: string[];
121
+ optional_permissions?: string[];
122
+ optional_host_permissions?: string[];
123
+ };
124
+ const declared = new Set<string>();
125
+ for (const surface of ["background", "sidepanel", "popup", "mcp"] as const) {
126
+ const stored = JSON.parse(await readFile(`dist/caps/${surface}.json`, "utf8")) as {
127
+ surface: string;
128
+ release: string;
129
+ protocolmajor: number;
130
+ permissions: string[];
131
+ };
132
+ expect(stored.surface).toBe(surface);
133
+ expect(stored.release).toBe(manifest.version);
134
+ expect(stored.protocolmajor).toBe(2);
135
+ expect(capmanifestof(surface).permissions).toEqual(stored.permissions);
136
+ for (const permission of stored.permissions) declared.add(permission);
137
+ }
138
+ const requested = [
139
+ ...manifest.permissions,
140
+ ...(manifest.optional_permissions ?? []),
141
+ ...(manifest.optional_host_permissions ?? []),
142
+ ];
143
+ for (const permission of requested) expect(declared.has(permission)).toBe(true);
144
+ for (const permission of declared) expect(requested.includes(permission)).toBe(true);
145
+ expect(capmanifestof("pagebridge").permissions).toEqual([]);
146
+ expect(capmanifestof("cli").permissions).toEqual([]);
147
+ expect(capmanifestof("library").permissions).toEqual([]);
148
+ });
149
+ });
150
+
151
+ /* ── The 1.1.95 strict content security policy of the security hardening release. ── */
152
+
153
+ describe("the strict content security policy", () => {
154
+ it("pins the extension pages of the root manifest and both overlays to local scripts and own framing", async () => {
155
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as {
156
+ content_security_policy?: { extension_pages?: string };
157
+ browsers?: Record<string, { content_security_policy?: { extension_pages?: string } }>;
158
+ };
159
+ const policy = manifest.content_security_policy?.extension_pages ?? "";
160
+ expect(policy).toBe("script-src 'self'; object-src 'self'; frame-ancestors 'self'");
161
+ expect(manifest.browsers?.firefox?.content_security_policy?.extension_pages).toBe(policy);
162
+ expect(manifest.browsers?.safari?.content_security_policy?.extension_pages).toBe(policy);
163
+ });
164
+
165
+ it("keeps every declared source free of wildcards, remote hosts, eval and inline", async () => {
166
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as {
167
+ content_security_policy?: { extension_pages?: string };
168
+ browsers?: Record<string, { content_security_policy?: { extension_pages?: string } }>;
169
+ };
170
+ const policies = [
171
+ manifest.content_security_policy?.extension_pages,
172
+ manifest.browsers?.firefox?.content_security_policy?.extension_pages,
173
+ manifest.browsers?.safari?.content_security_policy?.extension_pages,
174
+ ].filter((entry): entry is string => typeof entry === "string");
175
+ expect(policies).toHaveLength(3);
176
+ for (const policy of policies)
177
+ for (const source of policy.split(";").flatMap((directive) => directive.trim().split(/\s+/).slice(1))) {
178
+ expect(source.includes("*")).toBe(false);
179
+ expect(/^(https?|wss?|data):/i.test(source)).toBe(false);
180
+ expect(source.includes("unsafe-eval")).toBe(false);
181
+ expect(source.includes("unsafe-inline")).toBe(false);
182
+ }
183
+ });
184
+ });
185
+
186
+ /* ── The 1.1.96 multi agent certification: the dashboard surface permissions of the dashdone completion. ── */
187
+
188
+ describe("the multi agent dashboard surface", () => {
189
+ it("carries every multi agent panel of the dashdone completion inside the declared dashboardpage surface without new permissions", async () => {
190
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as {
191
+ permissions: string[];
192
+ optional_permissions?: string[];
193
+ chrome_url_overrides?: Record<string, string>;
194
+ };
195
+ const webdesign = await readFile("web/design.html", "utf8");
196
+ const dashboardmatch = /<template data-surface="dashboardpage">([\s\S]*?)<\/template>/.exec(webdesign);
197
+ expect(dashboardmatch).not.toBeNull();
198
+ const surface = dashboardmatch?.[1] ?? "";
199
+ /* every multi agent panel the dashdone completion ships rides the dashboardpage surface the manifest already declares as the newtab override */
200
+ for (const panel of [
201
+ "multiagentoverview",
202
+ "agentstatuscards",
203
+ "sharedqueueview",
204
+ "queuelanefilter",
205
+ "messageflow",
206
+ "conflictlog",
207
+ "agentcostpanel",
208
+ "escalationinbox",
209
+ "timelinescrubber",
210
+ "aggregatetimeline",
211
+ "killswitch",
212
+ "reportdownload",
213
+ ]) {
214
+ expect(surface.includes(`id="${panel}"`)).toBe(true);
215
+ }
216
+ /* the multi agent panels add no permission: the surface renders from the storage, the scripting and the active tab grants the dashboardpage already held */
217
+ expect(manifest.chrome_url_overrides).toEqual({ newtab: "dashboardpage.html" });
218
+ expect(manifest.permissions).toEqual(["activeTab", "storage", "scripting", "sidePanel"]);
219
+ expect((manifest.optional_permissions ?? []).includes("tabs")).toBe(true);
220
+ for (const reference of [...surface.matchAll(/(?:src|href)\s*=\s*["']([^"']+)["']/g)].map(
221
+ (match) => match[1] ?? "",
222
+ )) {
223
+ expect(/^(https?|wss?|data):/i.test(reference)).toBe(false);
224
+ }
225
+ });
226
+ });
@@ -0,0 +1,150 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ defaultmaskshapes,
4
+ fieldshapekind,
5
+ maskexport,
6
+ maskfield,
7
+ maskformstate,
8
+ maskmarker,
9
+ maskobservation,
10
+ maskrecord,
11
+ maskstoredvalues,
12
+ masktypedvalues,
13
+ maskvalue,
14
+ maskingfield,
15
+ shapesof,
16
+ } from "../security.js";
17
+ import type { maskrule, observation, runsettings, toolstep } from "../types.js";
18
+
19
+ const shapes = [...defaultmaskshapes];
20
+
21
+ function observationfixture(): observation {
22
+ return {
23
+ schemaversion: 3,
24
+ url: "https://example.com/login",
25
+ title: "Login",
26
+ textpreview: "Login",
27
+ textlength: 5,
28
+ forms: [
29
+ { label: "User", type: "text", name: "username" },
30
+ { label: "Password", type: "password", name: "password", options: ["hunter2"] },
31
+ ],
32
+ interactive: [],
33
+ capturedat: 1_000,
34
+ };
35
+ }
36
+
37
+ describe("maskinputs field shapes", () => {
38
+ it("recognizes the documented password, token, card and secret field shapes", () => {
39
+ expect(defaultmaskshapes).toEqual(["password", "token", "card", "secret"]);
40
+ expect(fieldshapekind("userpassword")).toBe("password");
41
+ expect(fieldshapekind("passwd")).toBe("password");
42
+ expect(fieldshapekind("pwdconfirm")).toBe("password");
43
+ expect(fieldshapekind("apitoken")).toBe("token");
44
+ expect(fieldshapekind("apikey")).toBe("token");
45
+ expect(fieldshapekind("authorization")).toBe("token");
46
+ expect(fieldshapekind("cardnumber")).toBe("card");
47
+ expect(fieldshapekind("cvv")).toBe("card");
48
+ expect(fieldshapekind("clientsecret")).toBe("secret");
49
+ expect(fieldshapekind("username")).toBeUndefined();
50
+ expect(maskingfield("password", [])).toBe(true);
51
+ expect(maskingfield("accountnumber", ["accountnumber"])).toBe(true);
52
+ expect(maskingfield("accountnumber", [])).toBe(false);
53
+ });
54
+
55
+ it("masks one value behind the redaction marker while an empty value stays empty", () => {
56
+ expect(maskmarker).toBe("[redacted]");
57
+ expect(maskvalue("hunter2")).toBe("[redacted]");
58
+ expect(maskvalue("")).toBe("");
59
+ expect(maskfield({ name: "password", value: "hunter2", shapes })).toBe("[redacted]");
60
+ expect(maskfield({ name: "username", value: "ada", shapes })).toBe("ada");
61
+ });
62
+
63
+ it("joins the documented families with the user shapes and the per origin mask rules", () => {
64
+ const settings: runsettings = { maskshapes: ["AccountNumber"] };
65
+ const rules: maskrule[] = [
66
+ { id: "r1", shapes: ["iban"], createdat: 1 },
67
+ { id: "r2", origin: "https://other.example", shapes: ["routing"], createdat: 1 },
68
+ ];
69
+ const effective = shapesof({ settings, rules, origin: "https://example.com" });
70
+ expect(effective).toContain("password");
71
+ expect(effective).toContain("accountnumber");
72
+ expect(effective).toContain("iban");
73
+ expect(effective).not.toContain("routing");
74
+ const scoped = shapesof({ settings, rules, origin: "https://other.example" });
75
+ expect(scoped).toContain("routing");
76
+ expect(shapesof({ rules: [] })).toEqual([...defaultmaskshapes]);
77
+ });
78
+ });
79
+
80
+ describe("maskinputs across record paths", () => {
81
+ it("masks typed values and option payloads of steps before they reach the log writer", () => {
82
+ const secret: toolstep = {
83
+ id: "s1",
84
+ kind: "type",
85
+ target: "input[name=password]",
86
+ value: "hunter2",
87
+ summary: "Type the password.",
88
+ risk: "sensitive",
89
+ options: JSON.stringify({ password: "hunter2", note: "plain" }),
90
+ };
91
+ const masked = masktypedvalues({ step: secret, shapes });
92
+ expect(masked.value).toBe("[redacted]");
93
+ expect(JSON.parse(masked.options ?? "{}")).toEqual({ password: "[redacted]", note: "plain" });
94
+ const plain: toolstep = {
95
+ id: "s2",
96
+ kind: "type",
97
+ target: "input[name=nickname]",
98
+ value: "ada",
99
+ summary: "Type the nickname.",
100
+ risk: "interaction",
101
+ };
102
+ const kept = masktypedvalues({ step: plain, shapes });
103
+ expect(kept.value).toBe("ada");
104
+ const malformed: toolstep = { ...plain, options: "{not json" };
105
+ expect(masktypedvalues({ step: malformed, shapes }).options).toBe("{not json");
106
+ });
107
+
108
+ it("masks form state while keeping the field shapes in observation payloads", () => {
109
+ const fields = [
110
+ { name: "username", value: "ada", type: "text" },
111
+ { name: "password", value: "hunter2", type: "password" },
112
+ ];
113
+ const masked = maskformstate(fields, shapes);
114
+ expect(masked[0]).toEqual({ name: "username", value: "ada", type: "text" });
115
+ expect(masked[1]?.value).toBe("[redacted]");
116
+ expect(masked[1]?.name).toBe("password");
117
+ const shot = maskobservation(observationfixture(), shapes);
118
+ expect(shot.forms[0]?.options).toBeUndefined();
119
+ expect(shot.forms[1]?.options).toEqual(["[redacted]"]);
120
+ expect(shot.forms[1]?.name).toBe("password");
121
+ expect(shot.forms[1]?.label).toBe("Password");
122
+ });
123
+
124
+ it("masks stored values and deep record payloads", () => {
125
+ const stored = maskstoredvalues({ sessiontoken: "tok", theme: "dark" }, shapes);
126
+ expect(stored.sessiontoken).toBe("[redacted]");
127
+ expect(stored.theme).toBe("dark");
128
+ const record = maskrecord(
129
+ { password: "hunter2", nested: { cardnumber: "4242", note: "plain" }, count: 3, flags: [1, 2] },
130
+ shapes,
131
+ );
132
+ expect(record.password).toBe("[redacted]");
133
+ expect((record.nested as Record<string, unknown>).cardnumber).toBe("[redacted]");
134
+ expect((record.nested as Record<string, unknown>).note).toBe("plain");
135
+ expect(record.count).toBe(3);
136
+ });
137
+
138
+ it("excludes masked values from every export of the run record", () => {
139
+ const runrecord = {
140
+ stepid: "s1",
141
+ summary: "The fill ran.",
142
+ details: { password: "hunter2", username: "ada", fields: [{ name: "card", value: "4242424242424242" }] },
143
+ };
144
+ const exported = maskexport(runrecord, shapes) as typeof runrecord;
145
+ expect(exported.details.password).toBe("[redacted]");
146
+ expect(exported.details.username).toBe("ada");
147
+ expect(exported.details.fields[0]?.name).toBe("card");
148
+ expect(exported.details.fields[0]?.value).toBe("[redacted]");
149
+ });
150
+ });
@@ -0,0 +1,111 @@
1
+ import { execFile } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { readFile } from "node:fs/promises";
4
+ import { promisify } from "node:util";
5
+ import { describe, expect, it } from "vitest";
6
+
7
+ const execute = promisify(execFile);
8
+
9
+ /* ── The release candidate verification matrix coverage. ── */
10
+
11
+ describe("the release candidate verification matrix artifact", () => {
12
+ it("declares every cell family the gate enumerates over the candidate surface", async () => {
13
+ const source = await readFile("tests/matrixverify.mjs", "utf8");
14
+ for (const family of ["kinds", "surfaces", "cli", "mcp", "migration", "importers", "gates"])
15
+ expect(source).toContain(`family: "${family}"`);
16
+ /* the gate covers the fake tab provider of every browser kind, the full kind catalog and the standing gates */
17
+ expect(source).toContain('["chromium", "firefox", "safari"]');
18
+ expect(source).toContain("actionkindcatalog");
19
+ expect(source).toContain("tests/artifacts/matrixverify.json");
20
+ expect(source).toContain("process.exitCode = 1");
21
+ });
22
+
23
+ it("covers every matrix cell with a passing outcome once the gate has run", async () => {
24
+ const artifactpath = "tests/artifacts/matrixverify.json";
25
+ const built = existsSync("dist/index.js");
26
+ const packagejson = JSON.parse(await readFile("package.json", "utf8")) as { version: string };
27
+ /* the matrix joins the recorded artifacts of the agent, cost, documentation and sweep gates beside its self contained subprocess cells, so the lane independence holds only where those artifacts exist: a built tree with its inputs present runs the gate itself, while a lane that skipped the earlier gates asserts the source declarations only and leaves the matrix verification to the gate chain that joins the matrix in order */
28
+ const inputs = [
29
+ "tests/artifacts/agentcert.json",
30
+ "tests/artifacts/costcert.json",
31
+ "tests/artifacts/doccheck.json",
32
+ "tests/artifacts/sweep.json",
33
+ ];
34
+ const inputsready = inputs.every((input) => existsSync(input));
35
+ if (built && inputsready && !existsSync(artifactpath)) await execute("node", ["tests/matrixverify.mjs"]);
36
+ if (built && inputsready && existsSync(artifactpath)) {
37
+ const stored = JSON.parse(await readFile(artifactpath, "utf8")) as { release: string };
38
+ if (stored.release !== packagejson.version) await execute("node", ["tests/matrixverify.mjs"]);
39
+ }
40
+ const stored = existsSync(artifactpath);
41
+ if (built && inputsready) expect(stored).toBe(true);
42
+ if (!stored) return;
43
+ const report = JSON.parse(await readFile(artifactpath, "utf8")) as {
44
+ release: string;
45
+ matrix: { browsers: string[]; kinds: number; cells: number };
46
+ cells: Array<{ family: string; name: string; outcome: string }>;
47
+ coverage: number;
48
+ summary: { total: number; passed: number; failed: number };
49
+ };
50
+ expect(report.release).toBe(packagejson.version);
51
+ expect(report.summary.failed).toBe(0);
52
+ expect(report.summary.passed).toBe(report.summary.total);
53
+ expect(report.coverage).toBe(100);
54
+ expect(report.matrix.browsers).toEqual(["chromium", "firefox", "safari"]);
55
+ expect(report.matrix.kinds).toBeGreaterThan(300);
56
+ expect(report.matrix.cells).toBe(report.summary.total);
57
+ /* the kind cells cover every browser kind of the fake tab provider */
58
+ for (const browser of report.matrix.browsers) {
59
+ expect(report.cells.some((cell) => cell.family === "kinds" && cell.name.includes(browser))).toBe(true);
60
+ }
61
+ /* the gate cells cover the poolaudit, the api freeze, the csp audit and the permission diff as subprocess runs, and the agentcert, costcert, doccheck and sweep artifacts as recorded evidence */
62
+ const gates = report.cells.filter((cell) => cell.family === "gates").map((cell) => cell.name);
63
+ for (const name of [
64
+ "the pool audit gate runs green over the 626 item pool",
65
+ "the api freeze gate runs green over the frozen contract",
66
+ "the csp audit gate runs green over the content security policy",
67
+ "the permission diff gate runs green over the versioned baseline",
68
+ "the agentcert artifact records a green run",
69
+ "the costcert artifact records a green run",
70
+ "the doccheck artifact records a green run",
71
+ "the sweep artifact records a green run",
72
+ ])
73
+ expect(gates).toContain(name);
74
+ /* the migration cell covers the version one window and the importer cells cover the fixture formats */
75
+ expect(report.cells.some((cell) => cell.family === "migration" && cell.name.includes("version one"))).toBe(true);
76
+ expect(report.cells.filter((cell) => cell.family === "importers").length).toBeGreaterThanOrEqual(3);
77
+ });
78
+ });
79
+
80
+ describe("the release candidate pool audit artifact", () => {
81
+ it("records the full pool disposition with no unknown item", async () => {
82
+ const artifactpath = "tests/artifacts/poolcoverage.json";
83
+ const built = existsSync("dist/index.js");
84
+ const packagejson = JSON.parse(await readFile("package.json", "utf8")) as { version: string };
85
+ if (built && !existsSync(artifactpath)) await execute("node", ["tests/poolaudit.mjs"]);
86
+ if (built && existsSync(artifactpath)) {
87
+ const stored = JSON.parse(await readFile(artifactpath, "utf8")) as { release: string };
88
+ if (stored.release !== packagejson.version) await execute("node", ["tests/poolaudit.mjs"]);
89
+ }
90
+ const stored = existsSync(artifactpath);
91
+ if (built) expect(stored).toBe(true);
92
+ if (!stored) return;
93
+ const report = JSON.parse(await readFile(artifactpath, "utf8")) as {
94
+ release: string;
95
+ corpus: number;
96
+ pool: { total: number; implemented: number; planned: number; unknown: number };
97
+ groups: Record<string, { total: number; implemented: number; planned: number; unknown: number }>;
98
+ summary: { failed: number };
99
+ };
100
+ expect(report.release).toBe(packagejson.version);
101
+ expect(report.summary.failed).toBe(0);
102
+ expect(report.pool.total).toBe(626);
103
+ expect(report.pool.unknown).toBe(0);
104
+ expect(report.pool.implemented).toBeGreaterThan(590);
105
+ expect(report.pool.implemented + report.pool.planned).toBe(report.pool.total);
106
+ const groupcount = Object.keys(report.groups).length;
107
+ expect(groupcount).toBeGreaterThan(10);
108
+ for (const group of Object.values(report.groups))
109
+ expect(group.total).toBe(group.implemented + group.planned + group.unknown);
110
+ });
111
+ });
@@ -0,0 +1,75 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { describe, expect, it } from "vitest";
3
+ import { mavenattachedartifacts, mavenjarresources } from "../pack.js";
4
+
5
+ describe("mavenpack", () => {
6
+ it("embeds every consumption mode as flat root resources inside the single distribution jar", () => {
7
+ expect(mavenjarresources()).toEqual([
8
+ "index.js",
9
+ "index.cjs",
10
+ "devthink.umd.js",
11
+ "checksums.txt",
12
+ "cli.js",
13
+ "headless.js",
14
+ "mcp.js",
15
+ "server.js",
16
+ "http.js",
17
+ ]);
18
+ });
19
+
20
+ it("attaches the declarations as classifier artifacts beside the extension zip", () => {
21
+ const attached = mavenattachedartifacts("1.1.99");
22
+ expect(attached).toEqual([
23
+ { classifier: "extension", type: "zip", path: "dist/devthink1.1.99.zip" },
24
+ { classifier: "declarations", type: "zip", path: "dist/devthink-declarations-1.1.99.zip" },
25
+ ]);
26
+ expect(() => mavenattachedartifacts("not-a-version")).toThrow();
27
+ });
28
+
29
+ it("records the devthink coordinates and the license the pom carries", async () => {
30
+ const rootpom = await readFile("pom.xml", "utf8");
31
+ expect(rootpom).toContain("<groupId>io.github.wenathlan</groupId>");
32
+ expect(rootpom).toContain("<artifactId>devthink</artifactId>");
33
+ expect(rootpom).toContain("<packaging>jar</packaging>");
34
+ expect(rootpom).toContain("<url>https://github.com/wenathlan/devthink</url>");
35
+ expect(rootpom).toContain("scm:git:https://github.com/wenathlan/devthink.git");
36
+ expect(rootpom).toContain("<name>GPL-3.0-only</name>");
37
+ expect(rootpom).toContain("<url>https://www.gnu.org/licenses/gpl-3.0.html</url>");
38
+ expect(rootpom).not.toContain("wenathlan/extension");
39
+ });
40
+
41
+ it("keeps the release version under the revision property so the resolver stamps one source", async () => {
42
+ const rootpom = await readFile("pom.xml", "utf8");
43
+ const packageversion = String(JSON.parse(await readFile("package.json", "utf8")).version);
44
+ expect(rootpom).toContain("<version>${revision}</version>");
45
+ expect(rootpom).toContain(`<revision>${packageversion}</revision>`);
46
+ });
47
+
48
+ it("publishes the single distribution to the GitHub Packages repository the pom declares", async () => {
49
+ const rootpom = await readFile("pom.xml", "utf8");
50
+ expect(rootpom).toContain("<distributionManagement>");
51
+ expect(rootpom).toContain("<id>github</id>");
52
+ expect(rootpom).toContain("https://maven.pkg.github.com/wenathlan/devthink");
53
+ });
54
+
55
+ it("renders the pom with the single jar packaging and the flat root resource includes of every mode", async () => {
56
+ const rootpom = await readFile("pom.xml", "utf8");
57
+ for (const resource of mavenjarresources()) expect(rootpom).toContain(`<include>${resource}</include>`);
58
+ expect(rootpom).not.toContain("<targetPath>");
59
+ expect(rootpom).not.toMatch(/[0-9a-f]{40,}/);
60
+ });
61
+
62
+ it("keeps the checked-in pom mirroring the maven layout so the descriptor never drifts", async () => {
63
+ const rootpom = await readFile("pom.xml", "utf8");
64
+ /* every attached classifier artifact of the pack module lands in the build helper the pom pins, resolved through the project properties so the attachment stays version driven */
65
+ for (const artifact of mavenattachedartifacts("1.1.99")) {
66
+ const attachedfile = "${project.basedir}/" + artifact.path.replace("1.1.99", "${project.version}");
67
+ expect(rootpom).toContain(`<file>${attachedfile}</file>`);
68
+ expect(rootpom).toContain(`<classifier>${artifact.classifier}</classifier>`);
69
+ }
70
+ expect(rootpom).toContain("<artifactId>build-helper-maven-plugin</artifactId>");
71
+ expect(rootpom).toContain("<version>3.6.1</version>");
72
+ expect(rootpom).toContain("<id>attach-extension-zip</id>");
73
+ expect(rootpom).toContain("<goal>attach-artifact</goal>");
74
+ });
75
+ });