@wenathlan/devthink 2.0.14 → 2.0.19

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 +60 -0
  2. package/{dist/bridge.d.ts → bridge.d.ts} +1 -1
  3. package/bridge.d.ts.map +1 -0
  4. package/bridge.js +1877 -0
  5. package/bun.d.ts.map +1 -0
  6. package/bun.js +52329 -0
  7. package/caps/background.json +712 -0
  8. package/caps/cli.json +360 -0
  9. package/caps/library.json +3611 -0
  10. package/caps/mcp.json +126 -0
  11. package/caps/pagebridge.json +377 -0
  12. package/caps/popup.json +51 -0
  13. package/caps/sidepanel.json +522 -0
  14. package/checksums.txt +142 -0
  15. package/cli.d.ts.map +1 -0
  16. package/{dist/cli.js → cli.js} +2 -2
  17. package/companion.d.ts.map +1 -0
  18. package/companion.js +389 -0
  19. package/crossbrowser.d.ts.map +1 -0
  20. package/crossbrowser.js +1191 -0
  21. package/dashdone.d.ts.map +1 -0
  22. package/dashdone.js +368 -0
  23. package/deno.d.ts.map +1 -0
  24. package/deno.js +52325 -0
  25. package/devthink.umd.js +52346 -0
  26. package/fixtures/bun.ts +7 -0
  27. package/fixtures/cjs.js +6 -0
  28. package/fixtures/deno.ts +6 -0
  29. package/fixtures/example-org-pagestate.json +22 -0
  30. package/fixtures/importers/automa-workflow.json +18 -0
  31. package/fixtures/importers/selenium-side.json +20 -0
  32. package/fixtures/importers/tabular-plan.csv +7 -0
  33. package/fixtures/importers/uivision-macro.json +10 -0
  34. package/fixtures/importers/v1-plan.json +13 -0
  35. package/fixtures/mcp-client.mjs +14 -0
  36. package/fixtures/pages/checkout.html +192 -0
  37. package/fixtures/pages/dashboard.html +110 -0
  38. package/fixtures/pages/feed.html +113 -0
  39. package/fixtures/pages/product-grid.html +150 -0
  40. package/fixtures/plans/form-inventory-plan.json +11 -0
  41. package/fixtures/plans/release-notes-plan.json +11 -0
  42. package/fixtures/recipes/agentscompete.json +11 -0
  43. package/fixtures/recipes/agentsforms.json +11 -0
  44. package/fixtures/recipes/agentsmonitor.json +11 -0
  45. package/fixtures/recipes/agentsreview.json +29 -0
  46. package/fixtures/recipes/agentswarm.json +11 -0
  47. package/fixtures/recipes/fillcalendar.json +12 -0
  48. package/fixtures/recipes/fillcheckout.json +13 -0
  49. package/fixtures/recipes/filldependentdropdowns.json +12 -0
  50. package/fixtures/recipes/fillfromcsv.json +13 -0
  51. package/fixtures/recipes/fillloginform.json +12 -0
  52. package/fixtures/recipes/fillmultistepwizard.json +13 -0
  53. package/fixtures/recipes/fillregistration.json +12 -0
  54. package/fixtures/recipes/fillsearchfilters.json +12 -0
  55. package/fixtures/recipes/filltypeahead.json +11 -0
  56. package/fixtures/recipes/monitoralerts.json +12 -0
  57. package/fixtures/recipes/monitoravailability.json +11 -0
  58. package/fixtures/recipes/monitornetworkquiet.json +12 -0
  59. package/fixtures/recipes/monitorpagechanges.json +11 -0
  60. package/fixtures/recipes/monitorpricechange.json +11 -0
  61. package/fixtures/recipes/monitorschedule.json +12 -0
  62. package/fixtures/recipes/scrapedynamicprices.json +13 -0
  63. package/fixtures/recipes/scrapeimagegallery.json +12 -0
  64. package/fixtures/recipes/scrapeinfinitefeed.json +13 -0
  65. package/fixtures/recipes/scrapenestedlists.json +12 -0
  66. package/fixtures/recipes/scrapeproductgrid.json +12 -0
  67. package/fixtures/recipes/scrapereviews.json +12 -0
  68. package/fixtures/recipes/scrapesearchresults.json +12 -0
  69. package/fixtures/recipes/scrapetablepagination.json +12 -0
  70. package/fixtures/recipes/testaccessibility.json +34 -0
  71. package/fixtures/recipes/testconsoleerrors.json +12 -0
  72. package/fixtures/recipes/testdeeplinks.json +12 -0
  73. package/fixtures/recipes/testformvalidation.json +13 -0
  74. package/fixtures/recipes/testlayoutshift.json +12 -0
  75. package/fixtures/recipes/testlinkcheck.json +12 -0
  76. package/fixtures/recipes/testloadtiming.json +12 -0
  77. package/fixtures/recipes/testvisualdiff.json +12 -0
  78. package/fixtures/workflows/changelog-digest-workflow.json +13 -0
  79. package/fixtures/workflows/form-survey-workflow.json +13 -0
  80. package/gallery.json +706 -0
  81. package/hardening.d.ts.map +1 -0
  82. package/hardening.js +188 -0
  83. package/headless.d.ts.map +1 -0
  84. package/headless.js +11522 -0
  85. package/http.d.ts.map +1 -0
  86. package/http.js +2165 -0
  87. package/index.cjs +54682 -0
  88. package/index.d.ts.map +1 -0
  89. package/index.js +52310 -0
  90. package/index.neutral.js +46343 -0
  91. package/{web/manifest.json → manifest.json} +1 -1
  92. package/mcp.d.ts.map +1 -0
  93. package/mcp.js +20515 -0
  94. package/memory.d.ts.map +1 -0
  95. package/{memory.ts → memory.js} +2310 -3834
  96. package/nativehost.template.json +10 -0
  97. package/node.cjs +54686 -0
  98. package/node.d.ts.map +1 -0
  99. package/pack.d.ts.map +1 -0
  100. package/pack.js +805 -0
  101. package/package.json +83 -176
  102. package/policy.d.ts.map +1 -0
  103. package/{policy.ts → policy.js} +4679 -5073
  104. package/progress.d.ts.map +1 -0
  105. package/progress.js +645 -0
  106. package/protocol.d.ts.map +1 -0
  107. package/protocol.js +8334 -0
  108. package/schemas/audit.schema.json +226 -0
  109. package/schemas/capability.schema.json +113 -0
  110. package/schemas/envelope.schema.json +162 -0
  111. package/schemas/memory.schema.json +51 -0
  112. package/schemas/observation.schema.json +113 -0
  113. package/schemas/plan.schema.json +208 -0
  114. package/schemas/progress.schema.json +126 -0
  115. package/schemas/proposal.schema.json +68 -0
  116. package/schemas/session.schema.json +82 -0
  117. package/schemas/tool.schema.json +94 -0
  118. package/server.d.ts.map +1 -0
  119. package/server.js +28877 -0
  120. package/umd-example.html +18 -0
  121. package/umd.d.ts.map +1 -0
  122. package/accounts.ts +0 -2087
  123. package/agent.ts +0 -2331
  124. package/antigravity.ts +0 -2139
  125. package/apifreeze.ts +0 -4146
  126. package/auth.ts +0 -642
  127. package/background.ts +0 -16837
  128. package/bridge.ts +0 -2080
  129. package/build.ts +0 -54
  130. package/bun.ts +0 -26
  131. package/capture.ts +0 -1614
  132. package/checksecrets.ts +0 -72
  133. package/cjs.ts +0 -9
  134. package/cli.ts +0 -3500
  135. package/commands.ts +0 -1101
  136. package/companion.ts +0 -450
  137. package/compatibility.ts +0 -37
  138. package/config.ts +0 -3137
  139. package/constants.ts +0 -943
  140. package/core.ts +0 -1473
  141. package/crossbrowser.ts +0 -1359
  142. package/dashdone.ts +0 -605
  143. package/data.ts +0 -943
  144. package/database.ts +0 -133
  145. package/debug.ts +0 -850
  146. package/deno.ts +0 -27
  147. package/devthink.ts +0 -707
  148. package/dist/accounts.d.ts +0 -409
  149. package/dist/agent.d.ts +0 -837
  150. package/dist/antigravity.d.ts +0 -380
  151. package/dist/apifreeze.d.ts +0 -113
  152. package/dist/auth.d.ts +0 -224
  153. package/dist/capture.d.ts +0 -595
  154. package/dist/cjs.d.ts +0 -6
  155. package/dist/commands.d.ts +0 -310
  156. package/dist/compatibility.d.ts +0 -17
  157. package/dist/config.d.ts +0 -1853
  158. package/dist/constants.d.ts +0 -637
  159. package/dist/core.d.ts +0 -361
  160. package/dist/data.d.ts +0 -330
  161. package/dist/database.d.ts +0 -24
  162. package/dist/debug.d.ts +0 -270
  163. package/dist/engine.d.ts +0 -74
  164. package/dist/environments.d.ts +0 -244
  165. package/dist/evidence.d.ts +0 -277
  166. package/dist/export.d.ts +0 -311
  167. package/dist/fingerprint.d.ts +0 -594
  168. package/dist/flow.d.ts +0 -300
  169. package/dist/gates.d.ts +0 -103
  170. package/dist/identity.d.ts +0 -51
  171. package/dist/ids.d.ts +0 -7
  172. package/dist/llm.d.ts +0 -652
  173. package/dist/models.d.ts +0 -1807
  174. package/dist/net.d.ts +0 -325
  175. package/dist/neutral.d.ts +0 -41
  176. package/dist/oauth.d.ts +0 -703
  177. package/dist/page.d.ts +0 -1166
  178. package/dist/perf.d.ts +0 -481
  179. package/dist/plan.d.ts +0 -134
  180. package/dist/project.d.ts +0 -395
  181. package/dist/providers.d.ts +0 -37
  182. package/dist/quota.d.ts +0 -453
  183. package/dist/recovery.d.ts +0 -621
  184. package/dist/request.d.ts +0 -1492
  185. package/dist/run.d.ts +0 -779
  186. package/dist/runtime.d.ts +0 -212
  187. package/dist/search.d.ts +0 -375
  188. package/dist/security.d.ts +0 -534
  189. package/dist/serve.d.ts +0 -136
  190. package/dist/session.d.ts +0 -296
  191. package/dist/storage.d.ts +0 -53
  192. package/dist/streaming.d.ts +0 -1119
  193. package/dist/swarm.d.ts +0 -328
  194. package/dist/tools.d.ts +0 -170
  195. package/dist/types.d.ts +0 -8357
  196. package/dist/utils.d.ts +0 -82
  197. package/dist/version.d.ts +0 -7
  198. package/dist/versionregistry.d.ts +0 -406
  199. package/dist/views.d.ts +0 -579
  200. package/dist/workbenchsession.d.ts +0 -69
  201. package/dist/workflow.d.ts +0 -649
  202. package/docs/readme.md +0 -92
  203. package/docs/schemas/antigravity.json +0 -192
  204. package/engine.ts +0 -1524
  205. package/environments.ts +0 -1005
  206. package/evidence.ts +0 -643
  207. package/export.ts +0 -889
  208. package/fingerprint.ts +0 -1458
  209. package/flow.ts +0 -1190
  210. package/gates.ts +0 -307
  211. package/gateway.ts +0 -34
  212. package/hardening.ts +0 -191
  213. package/headless.ts +0 -369
  214. package/http.ts +0 -2110
  215. package/identity.ts +0 -246
  216. package/ids.ts +0 -26
  217. package/index.ts +0 -369
  218. package/llm.ts +0 -2682
  219. package/mcp.ts +0 -1835
  220. package/models.ts +0 -3805
  221. package/modes.ts +0 -43
  222. package/net.ts +0 -1214
  223. package/neutral.ts +0 -362
  224. package/node.ts +0 -26
  225. package/oauth.ts +0 -2216
  226. package/offscreen.ts +0 -170
  227. package/pack.ts +0 -956
  228. package/page.ts +0 -7824
  229. package/pagebridge.ts +0 -1116
  230. package/perf.ts +0 -957
  231. package/plan.ts +0 -672
  232. package/plugin.ts +0 -76
  233. package/prisma.config.ts +0 -27
  234. package/progress.ts +0 -1308
  235. package/project.ts +0 -1291
  236. package/protocol.ts +0 -4187
  237. package/providers.ts +0 -246
  238. package/quota.ts +0 -1887
  239. package/recovery.ts +0 -2433
  240. package/request.ts +0 -4482
  241. package/run.ts +0 -2135
  242. package/runtime.ts +0 -526
  243. package/search.ts +0 -1760
  244. package/security.ts +0 -1673
  245. package/serve.ts +0 -419
  246. package/server.ts +0 -5538
  247. package/session.ts +0 -1162
  248. package/storage.ts +0 -182
  249. package/streaming.ts +0 -4101
  250. package/swarm.ts +0 -884
  251. package/sync.ts +0 -56
  252. package/tests/agentcert.test.ts +0 -128
  253. package/tests/agentfleet.test.ts +0 -214
  254. package/tests/agentreview.test.ts +0 -241
  255. package/tests/agentstream.test.ts +0 -335
  256. package/tests/agentwork.test.ts +0 -356
  257. package/tests/apifreeze.test.ts +0 -290
  258. package/tests/apimap.test.ts +0 -112
  259. package/tests/approvalgate.test.ts +0 -175
  260. package/tests/artifactmanifest.test.ts +0 -204
  261. package/tests/attentionfeed.test.ts +0 -163
  262. package/tests/backgroundruns.test.ts +0 -100
  263. package/tests/batchscheduling.test.ts +0 -131
  264. package/tests/blackboard.test.ts +0 -238
  265. package/tests/bridge.test.ts +0 -1088
  266. package/tests/browsercoverage.test.ts +0 -134
  267. package/tests/browserpolyfills.test.ts +0 -124
  268. package/tests/build.mjs +0 -1260
  269. package/tests/clientauth.test.ts +0 -223
  270. package/tests/clitools.test.ts +0 -578
  271. package/tests/confirmgates.test.ts +0 -190
  272. package/tests/containerpack.test.ts +0 -143
  273. package/tests/controlflow.test.ts +0 -2120
  274. package/tests/coordination.test.ts +0 -345
  275. package/tests/copyscreen.test.ts +0 -139
  276. package/tests/core.test.ts +0 -417
  277. package/tests/costcert.test.ts +0 -95
  278. package/tests/crossbrowser.test.ts +0 -115
  279. package/tests/cspaudit.test.ts +0 -79
  280. package/tests/dashdone.test.ts +0 -615
  281. package/tests/datagrid.test.ts +0 -137
  282. package/tests/doccheck.test.ts +0 -107
  283. package/tests/dombatch.test.ts +0 -109
  284. package/tests/emulation.test.ts +0 -282
  285. package/tests/environments.test.ts +0 -259
  286. package/tests/evidenceviews.test.ts +0 -88
  287. package/tests/exporttools.test.ts +0 -185
  288. package/tests/firefoxprep.test.ts +0 -134
  289. package/tests/flowlibrary.test.ts +0 -242
  290. package/tests/flowrun.test.ts +0 -303
  291. package/tests/forensics.test.ts +0 -439
  292. package/tests/gateway.test.ts +0 -939
  293. package/tests/headlesslib.test.ts +0 -171
  294. package/tests/httpstream.test.ts +0 -277
  295. package/tests/immutablelog.test.ts +0 -157
  296. package/tests/inboundguard.test.ts +0 -177
  297. package/tests/lazyload.test.ts +0 -72
  298. package/tests/librarymodes.test.ts +0 -623
  299. package/tests/llm.test.ts +0 -696
  300. package/tests/manifest.test.ts +0 -226
  301. package/tests/maskinputs.test.ts +0 -150
  302. package/tests/matrixverify.test.ts +0 -111
  303. package/tests/mavenpack.test.ts +0 -75
  304. package/tests/mcpserver.test.ts +0 -704
  305. package/tests/memory.test.ts +0 -4155
  306. package/tests/memorycare.test.ts +0 -230
  307. package/tests/migrateplan.test.ts +0 -349
  308. package/tests/minimization.test.ts +0 -414
  309. package/tests/modelroute.test.ts +0 -112
  310. package/tests/multiagent.test.ts +0 -440
  311. package/tests/nativebridge.test.ts +0 -807
  312. package/tests/navigation.test.ts +0 -284
  313. package/tests/nugetpack.test.ts +0 -83
  314. package/tests/orchestration.test.ts +0 -449
  315. package/tests/originpolicy.test.ts +0 -405
  316. package/tests/outputcompare.test.ts +0 -97
  317. package/tests/package.test.ts +0 -91
  318. package/tests/pagebridge.test.ts +0 -5909
  319. package/tests/pentest.test.ts +0 -75
  320. package/tests/perfrecords.test.ts +0 -257
  321. package/tests/permdiff.test.ts +0 -85
  322. package/tests/phishguard.test.ts +0 -128
  323. package/tests/pickerviews.test.ts +0 -155
  324. package/tests/pipelines.test.ts +0 -381
  325. package/tests/planlint.test.ts +0 -240
  326. package/tests/planreview.test.ts +0 -383
  327. package/tests/platformtargets.test.ts +0 -56
  328. package/tests/policy.test.ts +0 -8284
  329. package/tests/portability.test.ts +0 -96
  330. package/tests/progress.test.ts +0 -1036
  331. package/tests/promptlibrary.test.ts +0 -116
  332. package/tests/protocol.test.ts +0 -4793
  333. package/tests/publishingpipeline.test.ts +0 -314
  334. package/tests/quickactions.test.ts +0 -164
  335. package/tests/readiness.test.ts +0 -78
  336. package/tests/recipes.test.ts +0 -163
  337. package/tests/redactshots.test.ts +0 -216
  338. package/tests/registernvidiakeys.ts +0 -51
  339. package/tests/relayserve.test.ts +0 -304
  340. package/tests/releasenotes.test.ts +0 -276
  341. package/tests/resilience.test.ts +0 -467
  342. package/tests/resourceaware.test.ts +0 -187
  343. package/tests/runbudget.test.ts +0 -125
  344. package/tests/runhistory.test.ts +0 -299
  345. package/tests/runreplay.test.ts +0 -113
  346. package/tests/runresume.test.ts +0 -184
  347. package/tests/runstate.test.ts +0 -288
  348. package/tests/runtimeadapters.test.ts +0 -251
  349. package/tests/safariskeleton.test.ts +0 -115
  350. package/tests/sandboxframe.test.ts +0 -196
  351. package/tests/sbom.test.ts +0 -105
  352. package/tests/secretvault.test.ts +0 -188
  353. package/tests/selcache.test.ts +0 -111
  354. package/tests/sessioninterface.test.ts +0 -791
  355. package/tests/sessions.test.ts +0 -591
  356. package/tests/siteprefs.test.ts +0 -228
  357. package/tests/snapshotdelta.test.ts +0 -165
  358. package/tests/statusviews.test.ts +0 -237
  359. package/tests/stepmeter.test.ts +0 -200
  360. package/tests/streamparse.test.ts +0 -163
  361. package/tests/stylepruning.test.ts +0 -90
  362. package/tests/surfaces.test.ts +0 -282
  363. package/tests/sweep.test.ts +0 -78
  364. package/tests/syncbridge.test.ts +0 -164
  365. package/tests/taskqueue.test.ts +0 -206
  366. package/tests/toolcalls.test.ts +0 -336
  367. package/tests/toolcatalog.test.ts +0 -201
  368. package/tests/torture-agent.test.ts +0 -706
  369. package/tests/torture-auth.test.ts +0 -461
  370. package/tests/torture-commands.test.ts +0 -895
  371. package/tests/torture-data.test.ts +0 -923
  372. package/tests/torture-environments.test.ts +0 -285
  373. package/tests/torture-export.test.ts +0 -263
  374. package/tests/torture-http.test.ts +0 -1379
  375. package/tests/torture-llm.test.ts +0 -808
  376. package/tests/torture-memory.test.ts +0 -1084
  377. package/tests/torture-plan.test.ts +0 -829
  378. package/tests/torture-run.test.ts +0 -626
  379. package/tests/torture-session.test.ts +0 -928
  380. package/tests/torture-swarm.test.ts +0 -1245
  381. package/tests/torture-tools.test.ts +0 -964
  382. package/tests/torture-workflow.test.ts +0 -2354
  383. package/tests/tourviews.test.ts +0 -52
  384. package/tests/transparency.test.ts +0 -147
  385. package/tests/trigger.test.ts +0 -941
  386. package/tests/v1sunset.test.ts +0 -428
  387. package/tests/virtlist.test.ts +0 -94
  388. package/tests/vision.test.ts +0 -540
  389. package/tests/vsixpack.test.ts +0 -145
  390. package/tests/webapi.test.ts +0 -523
  391. package/tests/workflow.test.ts +0 -1785
  392. package/tests/workfloweditor.test.ts +0 -759
  393. package/tests/xpipack.test.ts +0 -128
  394. package/tools.ts +0 -734
  395. package/types.ts +0 -9908
  396. package/umd.ts +0 -9
  397. package/utils.ts +0 -249
  398. package/version.ts +0 -8
  399. package/versionregistry.ts +0 -1310
  400. package/views.ts +0 -2153
  401. package/vitest.config.ts +0 -12
  402. package/web/App.tsx +0 -78
  403. package/web/README.md +0 -47
  404. package/web/capacitor.config.ts +0 -46
  405. package/web/const.ts +0 -19
  406. package/web/control.shell.tsx +0 -66
  407. package/web/dashboardpage.ts +0 -554
  408. package/web/db.ts +0 -493
  409. package/web/design.html +0 -541
  410. package/web/gatewayview/Gateway.tsx +0 -345
  411. package/web/gatewayview/config.ts +0 -1042
  412. package/web/gatewayview/definition.ts +0 -174
  413. package/web/icons.ts +0 -15
  414. package/web/optionspage.ts +0 -691
  415. package/web/popup.ts +0 -1651
  416. package/web/schema.prisma +0 -176
  417. package/web/sidepanel.ts +0 -7941
  418. package/web/sitemanifest.json +0 -13
  419. package/web/terminal-ui.tsx +0 -622
  420. package/web/transparencypage.ts +0 -541
  421. package/web/vite.config.ts +0 -39
  422. package/web/workspace.ts +0 -23
  423. package/workbenchmemory.ts +0 -104
  424. package/workbenchsession.ts +0 -304
  425. package/workflow.ts +0 -4495
  426. /package/{dist/bun.d.ts → bun.d.ts} +0 -0
  427. /package/{dist/cli.d.ts → cli.d.ts} +0 -0
  428. /package/{dist/companion.d.ts → companion.d.ts} +0 -0
  429. /package/{dist/crossbrowser.d.ts → crossbrowser.d.ts} +0 -0
  430. /package/{dist/dashdone.d.ts → dashdone.d.ts} +0 -0
  431. /package/{dist/deno.d.ts → deno.d.ts} +0 -0
  432. /package/{dist/hardening.d.ts → hardening.d.ts} +0 -0
  433. /package/{dist/headless.d.ts → headless.d.ts} +0 -0
  434. /package/{dist/http.d.ts → http.d.ts} +0 -0
  435. /package/{dist/index.d.ts → index.d.ts} +0 -0
  436. /package/{dist/mcp.d.ts → mcp.d.ts} +0 -0
  437. /package/{dist/memory.d.ts → memory.d.ts} +0 -0
  438. /package/{dist/node.d.ts → node.d.ts} +0 -0
  439. /package/{dist/pack.d.ts → pack.d.ts} +0 -0
  440. /package/{dist/policy.d.ts → policy.d.ts} +0 -0
  441. /package/{dist/progress.d.ts → progress.d.ts} +0 -0
  442. /package/{dist/protocol.d.ts → protocol.d.ts} +0 -0
  443. /package/{dist/server.d.ts → server.d.ts} +0 -0
  444. /package/{dist/umd.d.ts → umd.d.ts} +0 -0
package/tests/build.mjs DELETED
@@ -1,1260 +0,0 @@
1
- /** Builds browser and library targets with no remote runtime dependencies. */
2
- import { build } from "esbuild";
3
- import { execFile } from "node:child_process";
4
- import { createHash } from "node:crypto";
5
- import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
6
- import { join } from "node:path";
7
- import { promisify } from "node:util";
8
- import { cjswrap } from "./cjswrap.mjs";
9
- import { stripstrings, underscorednames } from "./bundlescan.mjs";
10
- import { umdwrap } from "./umdwrap.mjs";
11
-
12
- /** Resolves the repo-local tsc bin (the merged repo rides bun; pnpm exec is retired). */
13
- async function resolveTsc() {
14
- const { createRequire } = await import("node:module");
15
- const require = createRequire(import.meta.url);
16
- const self = require.resolve("typescript/package.json");
17
- const path = await import("node:path");
18
- return path.join(path.dirname(self), "bin", "tsc");
19
- }
20
-
21
- const root = process.cwd();
22
- const extensiondist = join(root, "dist", "extension");
23
- const execute = promisify(execFile);
24
- const packagejson = JSON.parse(await readFile("package.json", "utf8"));
25
- await rm(join(root, "dist"), { recursive: true, force: true });
26
- await rm(extensiondist, { recursive: true, force: true });
27
- await mkdir(extensiondist, { recursive: true });
28
-
29
- /** The version stamp and license banner every bundle embeds: the version reads from package.json so no bundle carries a second version source, and the license name rides the same line. */
30
- const banner = `/*! devthink ${packagejson.version} — consent-first browser agent library — GPL-3.0-only — https://github.com/wenathlan/devthink */`;
31
-
32
- /** The byte budget of every dist target: the bounds are build-time engineering bounds on the unminified variants (a minified variant stays strictly smaller than its unminified bundle) and an overgrown bundle fails the build before it ships. The grand merge consolidation interned the provider gateway adapters in the llm core surface, so the index family bounds ride one notch above the pre-merge engineering ceilings the surface outgrew. */
33
- const bundlebudgets = {
34
- index: 2_200_000,
35
- indexcjs: 2_300_000,
36
- neutral: 2_000_000,
37
- umd: 2_300_000,
38
- node: 2_300_000,
39
- bun: 2_200_000,
40
- deno: 2_200_000,
41
- cli: 900_000,
42
- headless: 900_000,
43
- mcp: 1_400_000,
44
- server: 2_400_000,
45
- http: 600_000,
46
- bridge: 160_000,
47
- companion: 60_000,
48
- policy: 600_000,
49
- protocol: 380_000,
50
- memory: 500_000,
51
- progress: 60_000,
52
- hardening: 60_000,
53
- dashdone: 120_000,
54
- crossbrowser: 380_000,
55
- pack: 600_000,
56
- };
57
-
58
- /** Every dist bundle the build emits: the accounting, the naming check, the checksums file and the minified parity expectations read this one list. */
59
- const emitted = [];
60
-
61
- /** Records one emitted dist bundle: the list drives the size accounting, the naming check and the checksums file of every target. */
62
- function record(name) {
63
- emitted.push(name);
64
- return join("dist", name);
65
- }
66
-
67
- /** The shared core: one tree shakable esm entry every runtime re-exports, the cjs entry behind the stable getter object, the neutral browser bundle the browser condition selects, and one per module bundle for the policy, protocol, memory and progress module entries — every target with its minified variant beside it. */
68
- await build({
69
- entryPoints: ["index.ts"],
70
- outfile: record("index.js"),
71
- bundle: true,
72
- format: "esm",
73
- platform: "neutral",
74
- target: "es2022",
75
- sourcemap: true,
76
- banner: { js: banner },
77
- });
78
- await build({
79
- entryPoints: ["index.ts"],
80
- outfile: record("index.min.js"),
81
- bundle: true,
82
- format: "esm",
83
- platform: "neutral",
84
- target: "es2022",
85
- sourcemap: true,
86
- minify: true,
87
- banner: { js: banner },
88
- });
89
-
90
- /** The cjs mode: the build emits the core once, wraps the named exports behind the stable getter object and keeps the mode stamp the entry carries, so require consumers read the same surface the esm mode reads. */
91
- await build({
92
- entryPoints: ["cjs.ts"],
93
- outfile: join(root, "dist", "index.cjs"),
94
- bundle: true,
95
- format: "cjs",
96
- platform: "node",
97
- target: "node22",
98
- sourcemap: true,
99
- banner: { js: banner },
100
- });
101
- await writeFile(record("index.cjs"), cjswrap(await readFile(join(root, "dist", "index.cjs"), "utf8")), "utf8");
102
- await build({
103
- entryPoints: ["cjs.ts"],
104
- outfile: join(root, "dist", "index.min.cjs"),
105
- bundle: true,
106
- format: "cjs",
107
- platform: "node",
108
- target: "node22",
109
- sourcemap: true,
110
- minify: true,
111
- banner: { js: banner },
112
- });
113
- await writeFile(record("index.min.cjs"), cjswrap(await readFile(join(root, "dist", "index.min.cjs"), "utf8")), "utf8");
114
-
115
- /** The neutral target the browser condition selects: the browser platform build strips every node import because the whole core reaches the platform through the adapter seam, never through a direct import. */
116
- await build({
117
- entryPoints: ["neutral.ts"],
118
- outfile: record("index.neutral.js"),
119
- bundle: true,
120
- format: "esm",
121
- platform: "browser",
122
- target: "chrome120",
123
- sourcemap: true,
124
- banner: { js: banner },
125
- });
126
- await build({
127
- entryPoints: ["neutral.ts"],
128
- outfile: record("index.neutral.min.js"),
129
- bundle: true,
130
- format: "esm",
131
- platform: "browser",
132
- target: "chrome120",
133
- sourcemap: true,
134
- minify: true,
135
- banner: { js: banner },
136
- });
137
-
138
- /** The per module bundles of the exports map: policy, protocol, memory and progress ship as their own tree shakable entries with the types the declaration emit provides, the 1.1.95 hardening family ships its own entry beside them so the security gates stay importable without touching the frozen index surface, and the 1.1.96 dashdone family ships its own entry the same way so the multi agent dashboard view stays importable outside the frozen surface. */
139
- for (const modulename of ["policy", "protocol", "memory", "progress", "hardening", "dashdone"]) {
140
- await build({
141
- entryPoints: [`${modulename}.ts`],
142
- outfile: record(`${modulename}.js`),
143
- bundle: true,
144
- format: "esm",
145
- platform: "neutral",
146
- target: "es2022",
147
- sourcemap: true,
148
- banner: { js: banner },
149
- });
150
- await build({
151
- entryPoints: [`${modulename}.ts`],
152
- outfile: record(`${modulename}.min.js`),
153
- bundle: true,
154
- format: "esm",
155
- platform: "neutral",
156
- target: "es2022",
157
- sourcemap: true,
158
- minify: true,
159
- banner: { js: banner },
160
- });
161
- }
162
-
163
- /* the prisma client generates before the declaration emit: the database module imports the
164
- PrismaClient the generated .prisma/client tree declares, and a fresh install carries the
165
- stub until the generate step runs (the gateway lineage's build always generated first). */
166
- await execute(process.execPath, [join(root, "node_modules", "prisma", "build", "index.js"), "generate"], { cwd: root });
167
- await execute(process.execPath, [await resolveTsc(), "-p", "tsconfig.build.json"]);
168
-
169
- /** The terminal and headless entries of the 1.1.80 family keep their own dist targets beside the library modes, with the minified variants and the version banner riding the same accounting; the mcp server bundle of the 1.1.84 family serves the model context protocol surface of the serve mode as its own tree shakable entry, and the native bundles of the 1.1.85 family ship beside them: the wsbridge module bundle and the companion script the plain node recipe stamps from the companion sources. */
170
- await build({
171
- entryPoints: ["cli.ts"],
172
- outfile: record("cli.js"),
173
- bundle: true,
174
- format: "esm",
175
- platform: "node",
176
- target: "node22",
177
- sourcemap: true,
178
- banner: { js: `#!/usr/bin/env node\n${banner}` },
179
- });
180
- await build({
181
- entryPoints: ["cli.ts"],
182
- outfile: record("cli.min.js"),
183
- bundle: true,
184
- format: "esm",
185
- platform: "node",
186
- target: "node22",
187
- sourcemap: true,
188
- minify: true,
189
- banner: { js: `#!/usr/bin/env node\n${banner}` },
190
- });
191
- await build({
192
- entryPoints: ["headless.ts"],
193
- outfile: record("headless.js"),
194
- bundle: true,
195
- format: "esm",
196
- platform: "node",
197
- target: "node22",
198
- sourcemap: true,
199
- banner: { js: banner },
200
- });
201
- await build({
202
- entryPoints: ["headless.ts"],
203
- outfile: record("headless.min.js"),
204
- bundle: true,
205
- format: "esm",
206
- platform: "node",
207
- target: "node22",
208
- sourcemap: true,
209
- minify: true,
210
- banner: { js: banner },
211
- });
212
- await build({
213
- entryPoints: ["mcp.ts"],
214
- outfile: record("mcp.js"),
215
- bundle: true,
216
- format: "esm",
217
- platform: "node",
218
- target: "node22",
219
- sourcemap: true,
220
- banner: { js: banner },
221
- });
222
- await build({
223
- entryPoints: ["mcp.ts"],
224
- outfile: record("mcp.min.js"),
225
- bundle: true,
226
- format: "esm",
227
- platform: "node",
228
- target: "node22",
229
- sourcemap: true,
230
- minify: true,
231
- banner: { js: banner },
232
- });
233
-
234
- /** The bridge bundle of the 1.1.90 correlation wave: the native host bridge family — the wire contract, the site relay client, the chat widget, the localhost wsbridge relay and the native host surface — ships as one tree shakable entry with the minified variant beside it, so a local process imports the server contract, the relay, the chat bridge, the bind checks, the session tokens, the envelope translation, the port states, the handshake, the capability negotiation and the frame validation from one artifact. */
235
- await build({
236
- entryPoints: ["bridge.ts"],
237
- outfile: record("bridge.js"),
238
- bundle: true,
239
- format: "esm",
240
- platform: "neutral",
241
- target: "es2022",
242
- sourcemap: true,
243
- banner: { js: banner },
244
- });
245
- await build({
246
- entryPoints: ["bridge.ts"],
247
- outfile: record("bridge.min.js"),
248
- bundle: true,
249
- format: "esm",
250
- platform: "neutral",
251
- target: "es2022",
252
- sourcemap: true,
253
- minify: true,
254
- banner: { js: banner },
255
- });
256
-
257
- /** The cross browser bundles of the 1.1.86 browser coverage family: the webextension api map the gateway entry carried ships through the crossbrowser family entry, and the 1.1.90 correlation wave folds the polyfill layer, the firefoxprep adapter, the xpipack assembler and the safariskeleton wrapper into the one crossbrowser family entry with the minified variants beside them, so a local process imports the catalog, the namespace resolver, the firefox overlay adapter, the xpi assembler and the safari wrapper from one artifact beside the native bridge bundles. */
258
- await build({
259
- entryPoints: ["crossbrowser.ts"],
260
- outfile: record("crossbrowser.js"),
261
- bundle: true,
262
- format: "esm",
263
- platform: "neutral",
264
- target: "es2022",
265
- sourcemap: true,
266
- banner: { js: banner },
267
- });
268
- await build({
269
- entryPoints: ["crossbrowser.ts"],
270
- outfile: record("crossbrowser.min.js"),
271
- bundle: true,
272
- format: "esm",
273
- platform: "neutral",
274
- target: "es2022",
275
- sourcemap: true,
276
- minify: true,
277
- banner: { js: banner },
278
- });
279
- /** The server library entry of the grand merge consolidation: the gateway lineage library surface (the embedded gateway http transport, the request auth, the console cli and the library barrel) and the maene lineage barrel (the provider account console, the provider family namespaces and the opencode server plugin entry) interned in server.ts — one tree shakable entry answers the whole server surface contract, so the ./server subpath resolves from the flat package exactly like every other entry. The runtime dependencies the gateway persistence and the hono server pull (the prisma stack, libsql, hono, the node adapter, ink and react) ride the package.json dependencies of the published artifact — the entry marks them external so the bundled code stays the reviewed repository sources alone and the dependency tree the consumer installs answers the rest. */
280
- const libraryexternals = [
281
- "hono",
282
- "@hono/node-server",
283
- "@libsql/client",
284
- "@prisma/client",
285
- "@prisma/adapter-libsql",
286
- "ink",
287
- "react",
288
- "z-ai-web-dev-sdk",
289
- ];
290
- await build({
291
- entryPoints: ["server.ts"],
292
- outfile: record("server.js"),
293
- bundle: true,
294
- format: "esm",
295
- platform: "node",
296
- target: "node22",
297
- sourcemap: true,
298
- banner: { js: banner },
299
- external: libraryexternals,
300
- });
301
- await build({
302
- entryPoints: ["server.ts"],
303
- outfile: record("server.min.js"),
304
- bundle: true,
305
- format: "esm",
306
- platform: "node",
307
- target: "node22",
308
- sourcemap: true,
309
- minify: true,
310
- banner: { js: banner },
311
- external: libraryexternals,
312
- });
313
-
314
- /** The publishing pipeline bundles of the 1.1.87 family: the 1.1.90 correlation wave folds the vsixpack assembler, the mavenpack descriptors, the nugetpack layout, the containerpack stages, the sbom inventory builder and the artifact manifest builder into the one pack family entry, and the relayserve state machine ships beside them with the minified variants beside them, so the release chain and any local tooling import the packaging surfaces from one artifact beside the cross browser bundles. */
315
- for (const modulename of ["pack", "http"]) {
316
- await build({
317
- entryPoints: [`${modulename}.ts`],
318
- outfile: record(`${modulename}.js`),
319
- bundle: true,
320
- format: "esm",
321
- platform: "neutral",
322
- target: "es2022",
323
- sourcemap: true,
324
- banner: { js: banner },
325
- });
326
- await build({
327
- entryPoints: [`${modulename}.ts`],
328
- outfile: record(`${modulename}.min.js`),
329
- bundle: true,
330
- format: "esm",
331
- platform: "neutral",
332
- target: "es2022",
333
- sourcemap: true,
334
- minify: true,
335
- banner: { js: banner },
336
- });
337
- }
338
-
339
- /** The companionbin bundle of the 1.1.85 native host bridge family: the plain node recipe compiles the companion source of companion.ts with esbuild, stamps the package version over the source build marker and writes the runnable script with its sourcemap beside the host manifest template the same recipe stamps from the template the companion module embeds — no native compiler and no network step — and the packaging step asserts the recipe builds, because the companion ships as source plus a build command and never as a binary blob. */
340
- const companionbanner = `/*! devthink ${packagejson.version} companion — the optional native host of the native bridge — GPL-3.0-only — built with plain node, no native compiler */`;
341
- await build({
342
- entryPoints: ["companion.ts"],
343
- outfile: record("companion.js"),
344
- bundle: true,
345
- format: "esm",
346
- platform: "node",
347
- target: "node22",
348
- sourcemap: true,
349
- banner: { js: companionbanner },
350
- });
351
- const companionbundle = await readFile(join(root, "dist", "companion.js"), "utf8");
352
- const companionstamped = companionbundle.replace(
353
- /(const|var|let) companionbuild = "source"/,
354
- `$1 companionbuild = "${packagejson.version}"`,
355
- );
356
- if (companionstamped === companionbundle)
357
- throw new Error(
358
- "The companion build recipe found no source build marker to stamp; the companion keeps its companionbuild export.",
359
- );
360
- await writeFile(join(root, "dist", "companion.js"), companionstamped, "utf8");
361
- const companionmodule = await import(join(root, "dist", "companion.js"));
362
- const templatestamped = String(companionmodule.nativehosttemplatejson).replace(
363
- "__devthink_version__",
364
- packagejson.version,
365
- );
366
- if (templatestamped === String(companionmodule.nativehosttemplatejson))
367
- throw new Error(
368
- "The companion build recipe found no template banner placeholder to stamp; the template keeps its version placeholder.",
369
- );
370
- await writeFile(record("nativehost.template.json"), templatestamped, "utf8");
371
- console.log(
372
- `Companion built with plain node: dist/companion.js of build ${packagejson.version} with the sourcemap and the stamped host manifest template beside it.`,
373
- );
374
-
375
- /** The frozen contract artifacts of the 1.1.98 consolidation: the capability manifests of the seven frozen surfaces, the ten protocol schemas, the example fixture set and the umd example page are build artifacts the recipe derives from the repository sources — the caps come from the frozen lists the compiled library exports, the schemas and the fixtures copy verbatim from the tests/code sources, and the umd example rewrites its bundle reference to the dist root — so the packages ship them from dist while the repository root stays source only. The 2.0.0 example gallery extends the same fixture set: the 36 recipe plan files of tests/code/recipes copy into dist/fixtures/recipes as pure plan documents, the four self contained fixture pages of tests/code/pages copy into dist/fixtures/pages, and the gallery index (the metadata the recipes runner and the cli recipes command read) ships as dist/gallery.json beside the fixture set — outside the fixtures directory the headless fixture loader scans under schemastrict, so the recipes directory itself stays pure plans the planlint command walks while the index never answers a page state fixture probe. */
376
- const capsurfaces = ["background", "pagebridge", "sidepanel", "popup", "cli", "library", "mcp"];
377
- const librarymodule = await import(join(root, "dist", "index.js"));
378
- await mkdir(join(root, "dist", "caps"), { recursive: true });
379
- for (const surface of capsurfaces) {
380
- const manifest = librarymodule.capmanifestof(surface);
381
- if (manifest.release !== packagejson.version)
382
- throw new Error(
383
- `The capmanifest of the ${surface} surface pins the release ${manifest.release} while the package carries ${packagejson.version}.`,
384
- );
385
- await writeFile(record(`caps/${surface}.json`), `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
386
- }
387
- const schemadirectory = join(root, "tests", "code");
388
- const schemefiles = (await readdir(schemadirectory)).filter((file) => file.endsWith(".schema.json")).sort();
389
- if (schemefiles.length !== 10)
390
- throw new Error(`The protocol schema set of tests/code carries the ten frozen schemas, found ${schemefiles.length}.`);
391
- await mkdir(join(root, "dist", "schemas"), { recursive: true });
392
- for (const file of schemefiles)
393
- await writeFile(record(`schemas/${file}`), await readFile(join(schemadirectory, file)), "utf8");
394
- const fixtureset = [
395
- { source: "example-org-pagestate.json", target: "fixtures/example-org-pagestate.json" },
396
- { source: "mcp-client.mjs", target: "fixtures/mcp-client.mjs" },
397
- { source: "cjs.js", target: "fixtures/cjs.js" },
398
- { source: "bun.ts", target: "fixtures/bun.ts" },
399
- { source: "deno.ts", target: "fixtures/deno.ts" },
400
- { source: "form-inventory-plan.json", target: "fixtures/plans/form-inventory-plan.json" },
401
- { source: "release-notes-plan.json", target: "fixtures/plans/release-notes-plan.json" },
402
- { source: "changelog-digest-workflow.json", target: "fixtures/workflows/changelog-digest-workflow.json" },
403
- { source: "form-survey-workflow.json", target: "fixtures/workflows/form-survey-workflow.json" },
404
- { source: "importers/v1-plan.json", target: "fixtures/importers/v1-plan.json" },
405
- { source: "importers/automa-workflow.json", target: "fixtures/importers/automa-workflow.json" },
406
- { source: "importers/selenium-side.json", target: "fixtures/importers/selenium-side.json" },
407
- { source: "importers/uivision-macro.json", target: "fixtures/importers/uivision-macro.json" },
408
- { source: "importers/tabular-plan.csv", target: "fixtures/importers/tabular-plan.csv" },
409
- { source: "recipes/gallery.json", target: "gallery.json" },
410
- { source: "recipes/scrapeproductgrid.json", target: "fixtures/recipes/scrapeproductgrid.json" },
411
- { source: "recipes/scrapetablepagination.json", target: "fixtures/recipes/scrapetablepagination.json" },
412
- { source: "recipes/scrapereviews.json", target: "fixtures/recipes/scrapereviews.json" },
413
- { source: "recipes/scrapesearchresults.json", target: "fixtures/recipes/scrapesearchresults.json" },
414
- { source: "recipes/scrapenestedlists.json", target: "fixtures/recipes/scrapenestedlists.json" },
415
- { source: "recipes/scrapeinfinitefeed.json", target: "fixtures/recipes/scrapeinfinitefeed.json" },
416
- { source: "recipes/scrapedynamicprices.json", target: "fixtures/recipes/scrapedynamicprices.json" },
417
- { source: "recipes/scrapeimagegallery.json", target: "fixtures/recipes/scrapeimagegallery.json" },
418
- { source: "recipes/fillloginform.json", target: "fixtures/recipes/fillloginform.json" },
419
- { source: "recipes/fillcheckout.json", target: "fixtures/recipes/fillcheckout.json" },
420
- { source: "recipes/fillregistration.json", target: "fixtures/recipes/fillregistration.json" },
421
- { source: "recipes/fillsearchfilters.json", target: "fixtures/recipes/fillsearchfilters.json" },
422
- { source: "recipes/fillmultistepwizard.json", target: "fixtures/recipes/fillmultistepwizard.json" },
423
- { source: "recipes/filldependentdropdowns.json", target: "fixtures/recipes/filldependentdropdowns.json" },
424
- { source: "recipes/filltypeahead.json", target: "fixtures/recipes/filltypeahead.json" },
425
- { source: "recipes/fillcalendar.json", target: "fixtures/recipes/fillcalendar.json" },
426
- { source: "recipes/fillfromcsv.json", target: "fixtures/recipes/fillfromcsv.json" },
427
- { source: "recipes/testlinkcheck.json", target: "fixtures/recipes/testlinkcheck.json" },
428
- { source: "recipes/testconsoleerrors.json", target: "fixtures/recipes/testconsoleerrors.json" },
429
- { source: "recipes/testformvalidation.json", target: "fixtures/recipes/testformvalidation.json" },
430
- { source: "recipes/testlayoutshift.json", target: "fixtures/recipes/testlayoutshift.json" },
431
- { source: "recipes/testvisualdiff.json", target: "fixtures/recipes/testvisualdiff.json" },
432
- { source: "recipes/testloadtiming.json", target: "fixtures/recipes/testloadtiming.json" },
433
- { source: "recipes/testaccessibility.json", target: "fixtures/recipes/testaccessibility.json" },
434
- { source: "recipes/testdeeplinks.json", target: "fixtures/recipes/testdeeplinks.json" },
435
- { source: "recipes/monitorpricechange.json", target: "fixtures/recipes/monitorpricechange.json" },
436
- { source: "recipes/monitoravailability.json", target: "fixtures/recipes/monitoravailability.json" },
437
- { source: "recipes/monitornetworkquiet.json", target: "fixtures/recipes/monitornetworkquiet.json" },
438
- { source: "recipes/monitorpagechanges.json", target: "fixtures/recipes/monitorpagechanges.json" },
439
- { source: "recipes/monitorschedule.json", target: "fixtures/recipes/monitorschedule.json" },
440
- { source: "recipes/monitoralerts.json", target: "fixtures/recipes/monitoralerts.json" },
441
- { source: "recipes/agentswarm.json", target: "fixtures/recipes/agentswarm.json" },
442
- { source: "recipes/agentsreview.json", target: "fixtures/recipes/agentsreview.json" },
443
- { source: "recipes/agentsforms.json", target: "fixtures/recipes/agentsforms.json" },
444
- { source: "recipes/agentsmonitor.json", target: "fixtures/recipes/agentsmonitor.json" },
445
- { source: "recipes/agentscompete.json", target: "fixtures/recipes/agentscompete.json" },
446
- { source: "pages/product-grid.html", target: "fixtures/pages/product-grid.html" },
447
- { source: "pages/checkout.html", target: "fixtures/pages/checkout.html" },
448
- { source: "pages/dashboard.html", target: "fixtures/pages/dashboard.html" },
449
- { source: "pages/feed.html", target: "fixtures/pages/feed.html" },
450
- ];
451
- await mkdir(join(root, "dist", "fixtures", "plans"), { recursive: true });
452
- await mkdir(join(root, "dist", "fixtures", "workflows"), { recursive: true });
453
- await mkdir(join(root, "dist", "fixtures", "importers"), { recursive: true });
454
- await mkdir(join(root, "dist", "fixtures", "recipes"), { recursive: true });
455
- await mkdir(join(root, "dist", "fixtures", "pages"), { recursive: true });
456
- for (const fixture of fixtureset)
457
- await writeFile(record(fixture.target), await readFile(join(schemadirectory, fixture.source)), "utf8");
458
- const umdexample = await readFile(join(schemadirectory, "umd.html"), "utf8");
459
- const umdexamplestamped = `<!-- devthink ${packagejson.version} umd example page — GPL-3.0-only -->\n${umdexample.replace("../../dist/devthink.umd.js", "./devthink.umd.js")}`;
460
- if (!umdexamplestamped.endsWith(umdexample.replace("../../dist/devthink.umd.js", "./devthink.umd.js")))
461
- throw new Error(
462
- "The umd example source carries the dist bundle reference the build rewrites; the reference stayed unchanged.",
463
- );
464
- await writeFile(record("umd-example.html"), umdexamplestamped, "utf8");
465
- console.log(
466
- `Contract artifacts built: dist/caps of ${capsurfaces.length} surfaces, dist/schemas of ${schemefiles.length} frozen schemas, the dist/fixtures example set with the 2.0.0 example gallery (36 recipes, 4 fixture pages and the gallery index) and the dist/umd-example.html page.`,
467
- );
468
-
469
- /** The minified companion pair: the minifier reads the stamped recipe output and writes the minified variant with its own sourcemap beside it, so the checksum and the parity expectations of the dist contract cover the companion script like every other bundle. */
470
- await build({
471
- entryPoints: [join("dist", "companion.js")],
472
- outfile: record("companion.min.js"),
473
- bundle: true,
474
- format: "esm",
475
- platform: "node",
476
- target: "node22",
477
- sourcemap: true,
478
- minify: true,
479
- banner: { js: banner },
480
- });
481
-
482
- /** The bundle size accounting of every dist target: every target reports its byte size against its budget and an overgrown bundle fails the build. */
483
- const bundleaccounting = [];
484
- for (const [target, file] of [
485
- ["index", "index.js"],
486
- ["indexcjs", "index.cjs"],
487
- ["neutral", "index.neutral.js"],
488
- ["cli", "cli.js"],
489
- ["headless", "headless.js"],
490
- ["mcp", "mcp.js"],
491
- ["bridge", "bridge.js"],
492
- ["companion", "companion.js"],
493
- ["policy", "policy.js"],
494
- ["protocol", "protocol.js"],
495
- ["memory", "memory.js"],
496
- ["progress", "progress.js"],
497
- ["hardening", "hardening.js"],
498
- ["dashdone", "dashdone.js"],
499
- ["server", "server.js"],
500
- ["crossbrowser", "crossbrowser.js"],
501
- ["pack", "pack.js"],
502
- ["http", "http.js"],
503
- ]) {
504
- const bytes = (await stat(join(root, "dist", file))).size;
505
- bundleaccounting.push({ target, bytes, budget: bundlebudgets[target] });
506
- if (bytes > bundlebudgets[target])
507
- throw new Error(`The ${target} bundle of ${bytes} bytes exceeds its ${bundlebudgets[target]} byte budget.`);
508
- }
509
-
510
- /** The naming check keeps every unminified bundle free of underscored identifiers: the reviewed vocabulary declares no underscored name, so a bundle that grows one carries an unreviewed dependency; the scanner strips the string, comment and regex literals first and the ecmascript platform constants stay allowlisted because their names belong to the platform, not to the review. The minified variants stay outside the naming check because the minifier mangles nested scopes into underscored names and unquotes underscored object keys — the parity and checksum checks carry those variants instead. */
511
- for (const bundle of emitted.filter((name) => !name.includes(".min."))) {
512
- const content = stripstrings(await readFile(join(root, "dist", bundle), "utf8"));
513
- const underscored = underscorednames(content);
514
- if (underscored.length > 0)
515
- throw new Error(
516
- `The bundle ${bundle} carries the underscored identifier${underscored.length === 1 ? "" : "s"} ${[...new Set(underscored)].slice(0, 5).join(", ")}; the reviewed vocabulary carries none.`,
517
- );
518
- }
519
-
520
- /** The platform matrix drives the runtime shells: every declared target builds through its own adapter entry in one matrix run, each with its minified variant beside it. */
521
- await build({
522
- entryPoints: ["runtime.ts"],
523
- outfile: "dist/runtime.js",
524
- bundle: true,
525
- format: "esm",
526
- platform: "neutral",
527
- target: "es2022",
528
- });
529
- const { platformtargets, targetoutput, minifiedoutput } = await import(join(root, "dist/runtime.js"));
530
- const targets = platformtargets();
531
- for (const target of targets) {
532
- const output = join("dist", targetoutput(target));
533
- const minified = join("dist", minifiedoutput(target));
534
- if (target.format === "umd") {
535
- await build({
536
- entryPoints: [target.entry],
537
- outfile: join(root, "dist", "umd-cjs-core.cjs"),
538
- bundle: true,
539
- format: "cjs",
540
- platform: target.platform,
541
- target: "chrome120",
542
- sourcemap: false,
543
- });
544
- const body = await readFile(join(root, "dist", "umd-cjs-core.cjs"), "utf8");
545
- await writeFile(record(targetoutput(target)), `${banner}\n${umdwrap(body, "devthink")}`, "utf8");
546
- await writeFile(
547
- `${output}.map`,
548
- JSON.stringify({ version: 3, file: targetoutput(target), sources: [target.entry], sourcesContent: [body] }),
549
- "utf8",
550
- );
551
- await rm(join(root, "dist", "umd-cjs-core.cjs"), { force: true });
552
- await build({
553
- entryPoints: [target.entry],
554
- outfile: join(root, "dist", "umd-cjs-core.cjs"),
555
- bundle: true,
556
- format: "cjs",
557
- platform: target.platform,
558
- target: "chrome120",
559
- sourcemap: false,
560
- minify: true,
561
- });
562
- const minbody = await readFile(join(root, "dist", "umd-cjs-core.cjs"), "utf8");
563
- await writeFile(record(minifiedoutput(target)), `${banner}\n${umdwrap(minbody, "devthink")}`, "utf8");
564
- await writeFile(
565
- `${minified}.map`,
566
- JSON.stringify({ version: 3, file: minifiedoutput(target), sources: [target.entry], sourcesContent: [minbody] }),
567
- "utf8",
568
- );
569
- await rm(join(root, "dist", "umd-cjs-core.cjs"), { force: true });
570
- } else {
571
- await build({
572
- entryPoints: [target.entry],
573
- outfile: record(targetoutput(target)),
574
- bundle: true,
575
- format: target.format,
576
- platform: target.platform,
577
- target: target.platform === "browser" ? "chrome120" : target.platform === "node" ? "node22" : "es2022",
578
- sourcemap: true,
579
- banner: { js: banner },
580
- });
581
- await build({
582
- entryPoints: [target.entry],
583
- outfile: record(minifiedoutput(target)),
584
- bundle: true,
585
- format: target.format,
586
- platform: target.platform,
587
- target: target.platform === "browser" ? "chrome120" : target.platform === "node" ? "node22" : "es2022",
588
- sourcemap: true,
589
- minify: true,
590
- banner: { js: banner },
591
- });
592
- }
593
- }
594
- await rm(join(root, "dist", "runtime.js"), { force: true });
595
- await rm(join(root, "dist", "runtime.js.map"), { force: true });
596
-
597
- /** The matrix bundles join the size accounting with their own budgets: the node shell rides the cjs budget and the bun and deno shells ride the esm budgets of their sizes. */
598
- for (const target of targets) {
599
- const name = target.format === "umd" ? "umd" : target.runtime;
600
- const bytes = (await stat(join(root, "dist", targetoutput(target)))).size;
601
- bundleaccounting.push({ target: name, bytes, budget: bundlebudgets[name] });
602
- if (bytes > bundlebudgets[name])
603
- throw new Error(`The ${name} bundle of ${bytes} bytes exceeds its ${bundlebudgets[name]} byte budget.`);
604
- }
605
-
606
- /** The matrix bundles join the naming check after their emission, so every runtime shell carries the reviewed vocabulary the core carries. */
607
- for (const target of targets) {
608
- const content = stripstrings(await readFile(join(root, "dist", targetoutput(target)), "utf8"));
609
- const underscored = underscorednames(content);
610
- if (underscored.length > 0)
611
- throw new Error(
612
- `The bundle ${targetoutput(target)} carries the underscored identifier${underscored.length === 1 ? "" : "s"} ${[...new Set(underscored)].slice(0, 5).join(", ")}; the reviewed vocabulary carries none.`,
613
- );
614
- }
615
-
616
- /* The surface entries of the grand-merge layout: background and offscreen stay
617
- root modules (the engine imports them), the pure interface surfaces ride
618
- web/ (the interface tree standard). */
619
- const surfaceentry = (name) =>
620
- name === "background" || name === "offscreen" ? `${name}.ts` : `web/${name}.ts`;
621
- await Promise.all(
622
- ["background", "popup", "sidepanel", "offscreen", "transparencypage", "dashboardpage", "optionspage"].map((name) =>
623
- build({
624
- entryPoints: [surfaceentry(name)],
625
- outfile: `dist/extension/${name}.js`,
626
- bundle: true,
627
- format: "esm",
628
- platform: "browser",
629
- target: "chrome120",
630
- sourcemap: true,
631
- }),
632
- ),
633
- );
634
- await build({
635
- entryPoints: ["pagebridge.ts"],
636
- outfile: "dist/extension/pagebridge.js",
637
- bundle: true,
638
- format: "iife",
639
- platform: "browser",
640
- target: "chrome120",
641
- sourcemap: true,
642
- });
643
- /** The source manifest of the 1.1.93 single manifest design: the firefox, safari and vsix overlays ride the root manifest.json under the browsers and vsix keys, one manifest speaks every webextension dialect and no second hand maintained manifest file exists; the version the root manifest carries stays the single version source every overlay inherits. */
644
- const sourcemanifest = JSON.parse(await readFile(join(root, "web/manifest.json"), "utf8"));
645
- /** The extension copy of the root manifest: the shipped chromium manifest drops the browsers and vsix metadata keys because the derived browser manifests the artifacts carry never include the overlay metadata of the other targets. */
646
- const extensionmanifest = { ...sourcemanifest };
647
- delete extensionmanifest.browsers;
648
- delete extensionmanifest.vsix;
649
- await writeFile(join(extensiondist, "manifest.json"), `${JSON.stringify(extensionmanifest, null, 2)}\n`, "utf8");
650
- /** The web surface step of the 1.1.88 consolidation: the one design file web/design.html carries every surface template and the shared stylesheet embedded, and the build splits the templates into the per-surface extension pages with the stylesheet written beside them, so the extension surfaces and the deployed site render from one design. */
651
- const webdir = join(root, "web");
652
- const webindex = await readFile(join(webdir, "design.html"), "utf8");
653
- const extensionstyle = /<style data-source="extension">([\s\S]*?)<\/style>/.exec(webindex)?.[1]?.trim() ?? "";
654
- const surfacematches = [...webindex.matchAll(/<template data-surface="([^"]+)">([\s\S]*?)<\/template>/g)].map(
655
- (match) => ({ name: match[1], body: match[2].trim() }),
656
- );
657
- const surfacetitles = {
658
- popup: "Devthink",
659
- sidepanel: "Devthink review",
660
- dashboardpage: "Devthink dashboard",
661
- optionspage: "Devthink options",
662
- transparencypage: "Devthink transparency",
663
- sandbox: "Devthink sandbox frame",
664
- offscreen: "Devthink offscreen",
665
- };
666
- const styledsurfaces = new Set(["popup", "sidepanel", "dashboardpage", "optionspage", "transparencypage"]);
667
- if (extensionstyle === "")
668
- throw new Error("The web design file carries the embedded extension stylesheet; the build found none.");
669
- await writeFile(join(extensiondist, "style.css"), `${extensionstyle}\n`, "utf8");
670
- for (const surface of surfacematches) {
671
- if (surface.name === "site") continue; /* the site surface deploys from web/ as the static site */
672
- const title = surfacetitles[surface.name];
673
- if (title === undefined)
674
- throw new Error(`The web design file carries the surface ${surface.name} without a known extension page title.`);
675
- const stylelink = styledsurfaces.has(surface.name) ? '<link rel="stylesheet" href="style.css">' : "";
676
- await writeFile(
677
- join(extensiondist, `${surface.name}.html`),
678
- `<!doctype html>\n<html lang="en">\n<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${title}</title>${stylelink}</head>\n<body>\n${surface.body}\n</body>\n</html>\n`,
679
- "utf8",
680
- );
681
- }
682
- for (const required of [
683
- "popup",
684
- "sidepanel",
685
- "sandbox",
686
- "offscreen",
687
- "dashboardpage",
688
- "optionspage",
689
- "transparencypage",
690
- ]) {
691
- if (!surfacematches.some((surface) => surface.name === required))
692
- throw new Error(`The web design file misses the ${required} surface template the manifest declares.`);
693
- }
694
-
695
- /** The icon family of the 2.0.2 final polish: the root icons.ts module carries the store-ready icon set as base64 png payloads — the repository stays text-only — and the build materializes them into the extension zip directory the manifest icons block, the action default icon and the notification icon path resolve against. Every payload is verified at build time: it decodes to a real png header, its ihdr width and height match the size key it rides under, and the manifest icon paths agree with the materialized files, so the icon set provably renders at the required sizes before any artifact ships. */
696
- const iconssource = await readFile(join(root, "web/icons.ts"), "utf8");
697
- const iconblock = /export const iconpayloads: Record<string, string> = \{([\s\S]*?)\};/.exec(iconssource)?.[1] ?? "";
698
- const iconentries = [...iconblock.matchAll(/"(\d+)":\s*"([A-Za-z0-9+/=]+)"/g)].map((match) => ({
699
- size: Number(match[1]),
700
- base64: match[2],
701
- }));
702
- if (iconentries.length !== 6)
703
- throw new Error(
704
- `The icon family of the final polish carries six required sizes; the icons module declares ${iconentries.length}.`,
705
- );
706
- const iconsizes = new Set(iconentries.map((entry) => entry.size));
707
- for (const size of [16, 19, 32, 38, 48, 128])
708
- if (!iconsizes.has(size)) throw new Error(`The icon family misses the required ${size} pixel size.`);
709
- await mkdir(join(extensiondist, "icons"), { recursive: true });
710
- for (const entry of iconentries) {
711
- const bytes = Buffer.from(entry.base64, "base64");
712
- const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
713
- if (bytes.subarray(0, 8).equals(signature) === false)
714
- throw new Error(`The ${entry.size} pixel icon payload does not decode to a png header.`);
715
- const width = bytes.readUInt32BE(16);
716
- const height = bytes.readUInt32BE(20);
717
- if (width !== entry.size || height !== entry.size)
718
- throw new Error(
719
- `The ${entry.size} pixel icon payload decodes to ${width}x${height}; every icon renders at its declared size.`,
720
- );
721
- if (extensionmanifest.icons?.[String(entry.size)] !== `icons/${entry.size}.png`)
722
- throw new Error(`The manifest icons block does not resolve the ${entry.size} pixel icon the build materializes.`);
723
- await writeFile(join(extensiondist, "icons", `${entry.size}.png`), bytes);
724
- }
725
- for (const size of Object.keys(extensionmanifest.icons ?? {}))
726
- if (!iconsizes.has(Number(size)))
727
- throw new Error(`The manifest icons block references the ${size} pixel size the icons module does not carry.`);
728
- if (extensionmanifest.action?.default_icon?.["128"] !== "icons/128.png")
729
- throw new Error("The action default icon block does not resolve the 128 pixel toolbar icon the build materializes.");
730
-
731
- /** The extension bundle and the browser library bundles stay free of node shims: the runtime entries reach the platform through the adapter seam only, so neither the extension targets nor the neutral and umd library modes ever leak a node import into a browser bundle. */
732
- const nodeshim = /(?:from\s*|require\(\s*)["']node:/;
733
- for (const name of [
734
- "background",
735
- "popup",
736
- "sidepanel",
737
- "offscreen",
738
- "pagebridge",
739
- "transparencypage",
740
- "dashboardpage",
741
- "optionspage",
742
- ]) {
743
- const content = await readFile(join(extensiondist, `${name}.js`), "utf8");
744
- if (nodeshim.test(content))
745
- throw new Error(
746
- `The extension bundle ${name}.js carries a node shim; the browser targets stay free of node imports.`,
747
- );
748
- }
749
- for (const name of ["index.neutral.js", "index.neutral.min.js", "devthink.umd.js", "devthink.umd.min.js"]) {
750
- const content = await readFile(join(root, "dist", name), "utf8");
751
- if (nodeshim.test(content))
752
- throw new Error(
753
- `The browser library bundle ${name} carries a node shim; the browser condition stays free of node imports.`,
754
- );
755
- }
756
-
757
- /** The declaration emit covers every public module: the matrix targets keep their declaration files and the policy, protocol, memory, progress and hardening module entries carry the declaration files the exports map types conditions read. */
758
- for (const target of targets) {
759
- const declaration = await readFile(join(root, "dist", `${target.entry.replace(/\.ts$/, "")}.d.ts`), "utf8");
760
- if (declaration.trim() === "") throw new Error(`The ${target.runtime} platform target lacks its declaration file.`);
761
- }
762
- for (const entry of [
763
- "index",
764
- "cjs",
765
- "neutral",
766
- "policy",
767
- "protocol",
768
- "memory",
769
- "progress",
770
- "hardening",
771
- "dashdone",
772
- "types",
773
- "runtime",
774
- "umd",
775
- "node",
776
- "bun",
777
- "deno",
778
- "headless",
779
- "cli",
780
- "agent",
781
- "auth",
782
- "http",
783
- "server",
784
- "page",
785
- "mcp",
786
- "serve",
787
- "tools",
788
- "bridge",
789
- "crossbrowser",
790
- "pack",
791
- "companion",
792
- "version",
793
- ]) {
794
- const declaration = await readFile(join(root, "dist", `${entry}.d.ts`), "utf8");
795
- if (declaration.trim() === "") throw new Error(`The ${entry} module entry lacks its declaration file.`);
796
- }
797
-
798
- /** The staticdeploy step of the 1.1.82 site integration family: the plain static assets of site/ build to dist/site with content hashed file names (the index rewrites its asset references to the hashed names), an immutable cache header configuration rides beside them as a plain static _headers file with zero server functions, no edge functions, no redirect rules and no vendor runtime, and the site zip ships as its own release artifact instead of entering the package exports map. */
799
- const siteaccounting = [];
800
- const sitehashof = (bytes) => createHash("sha256").update(bytes).digest("hex").slice(0, 10);
801
- const sitedir = join(root, "web");
802
- const sitedist = join(root, "dist", "site");
803
- await mkdir(sitedist, { recursive: true });
804
- let siteindex = await readFile(join(sitedir, "design.html"), "utf8");
805
- const headerlines = ["/index.html", " Cache-Control: no-cache"];
806
- siteaccounting.push({ file: "index.html", bytes: siteindex.length, budget: 300_000 });
807
- if (siteindex.length > 300_000)
808
- throw new Error(`The one design file of ${siteindex.length} bytes exceeds its 300000 byte budget.`);
809
- for (const asset of ["sitemanifest.json"]) {
810
- const bytes = await readFile(join(sitedir, asset));
811
- const hashed = asset.replace(/(\.[^.]+)$/, `.${sitehashof(bytes)}$1`);
812
- await writeFile(join(sitedist, hashed), bytes);
813
- emitted.push(`site/${hashed}`);
814
- siteindex = siteindex.split(asset).join(hashed);
815
- headerlines.push(`/${hashed}`, " Cache-Control: public, max-age=31536000, immutable");
816
- const bytesize = bytes.length;
817
- siteaccounting.push({ file: hashed, bytes: bytesize, budget: 100_000 });
818
- if (bytesize > 100_000)
819
- throw new Error(`The site asset ${asset} of ${bytesize} bytes exceeds its 100000 byte budget.`);
820
- }
821
- await writeFile(join(sitedist, "index.html"), siteindex, "utf8");
822
- emitted.push("site/index.html");
823
- await writeFile(join(sitedist, "_headers"), `${headerlines.join("\n")}\n`, "utf8");
824
- emitted.push("site/_headers");
825
- const sitebytes = siteaccounting.reduce((total, entry) => total + entry.bytes, 0);
826
- if (sitebytes > 400_000) throw new Error(`The static site of ${sitebytes} bytes exceeds its 400000 byte total budget.`);
827
- const sitefiletypes = [
828
- ...new Set(
829
- (await readdir(sitedist)).map((file) => (file.startsWith("_") ? file : file.slice(file.lastIndexOf(".") + 1))),
830
- ),
831
- ];
832
- if (!sitefiletypes.every((type) => ["html", "css", "js", "json", "_headers"].includes(type)))
833
- throw new Error(`The static site output carries a non static file type: ${sitefiletypes.join(", ")}.`);
834
- for (const file of await readdir(sitedist)) {
835
- const underscored = underscorednames(stripstrings(await readFile(join(sitedist, file), "utf8")));
836
- if (underscored.length > 0)
837
- throw new Error(
838
- `The site file ${file} carries the underscored identifier${underscored.length === 1 ? "" : "s"} ${[...new Set(underscored)].slice(0, 5).join(", ")}; the reviewed site vocabulary carries none.`,
839
- );
840
- }
841
-
842
- /** The extension gallery copy of the 2.0.0 example gallery: the chromium extension archive carries the gallery index and its recipe plans under fixtures/ so the sidepanel one click import reads them through the runtime url of the packaged extension — the plans stay pure plan documents, the fixture pages stay package fixtures the recipes name, and no recipe grows a remote origin. */
843
- const extensionfixtures = join(extensiondist, "fixtures", "recipes");
844
- await mkdir(extensionfixtures, { recursive: true });
845
- await writeFile(
846
- join(extensiondist, "fixtures", "gallery.json"),
847
- await readFile(join(root, "tests", "code", "recipes", "gallery.json")),
848
- "utf8",
849
- );
850
- for (const file of (await readdir(join(root, "tests", "code", "recipes"))).filter(
851
- (file) => file.endsWith(".json") && file !== "gallery.json",
852
- )) {
853
- await writeFile(join(extensionfixtures, file), await readFile(join(root, "tests", "code", "recipes", file)), "utf8");
854
- }
855
-
856
- /** The chromium extension zip of the release: the build packs the extension dist directory into the version stamped archive the release workflow attaches beside every other artifact. */
857
- await rm(join(root, "dist", `devthink${packagejson.version}.zip`), { force: true });
858
- await execute("zip", ["-qr", `../devthink${packagejson.version}.zip`, "."], { cwd: extensiondist });
859
-
860
- /** The firefox target of the 1.1.86 browser coverage family: the build reads the source manifest, layers the firefox overlay (the browsers.firefox section of the root manifest.json) on top through the firefoxprep adapter, assembles the xpi through the xpipack assembler with the adapted manifest and the hashed extension dist bundles, and writes the artifact beside the chromium extension zip; the artifact name carries the release version, the manifest placement sits at the archive root, and the addons linter markers stay inside the budget the build asserts. */
861
- const firefoxoverlay = sourcemanifest.browsers?.firefox;
862
- if (firefoxoverlay === undefined)
863
- throw new Error(
864
- "The root manifest carries the firefox overlay under the browsers key; a missing overlay never builds the firefox target.",
865
- );
866
- const firefoxprep = await import(join(root, "dist", "crossbrowser.js"));
867
- const xpipack = await import(join(root, "dist", "crossbrowser.js"));
868
- const firefoxadapted = firefoxprep.firefoxprepadapt({
869
- manifest: sourcemanifest,
870
- overlay: firefoxoverlay,
871
- backgroundscripts: ["background.js"],
872
- });
873
- const firefoxbundleentries = [];
874
- const firefoxhashof = (bytes) => createHash("sha256").update(bytes).digest("hex").slice(0, 10);
875
- for (const name of [
876
- "background.js",
877
- "popup.js",
878
- "sidepanel.js",
879
- "pagebridge.js",
880
- "offscreen.js",
881
- "transparencypage.js",
882
- "dashboardpage.js",
883
- "optionspage.js",
884
- ]) {
885
- try {
886
- const bytes = await readFile(join(extensiondist, name));
887
- const hashed = name.replace(/(\.[^.]+)$/, `.${firefoxhashof(bytes)}$1`);
888
- await writeFile(join(extensiondist, hashed), bytes);
889
- firefoxbundleentries.push({ name: hashed, bytes: new Uint8Array(bytes) });
890
- } catch {
891
- /* the page bridge bundle is optional on the firefox target */
892
- }
893
- }
894
- for (const asset of [
895
- "popup.html",
896
- "offscreen.html",
897
- "sandbox.html",
898
- "sidepanel.html",
899
- "transparencypage.html",
900
- "dashboardpage.html",
901
- "optionspage.html",
902
- "style.css",
903
- "icons/16.png",
904
- "icons/19.png",
905
- "icons/32.png",
906
- "icons/38.png",
907
- "icons/48.png",
908
- "icons/128.png",
909
- ]) {
910
- try {
911
- const bytes = await readFile(join(extensiondist, asset));
912
- firefoxbundleentries.push({ name: asset, bytes: new Uint8Array(bytes) });
913
- } catch {
914
- /* the firefox target carries the assets the chromium build ships */
915
- }
916
- }
917
- const xpibuilt = xpipack.xpipackassemble({
918
- manifest: firefoxadapted.manifest,
919
- bundleentries: firefoxbundleentries,
920
- version: packagejson.version,
921
- });
922
- await writeFile(join(root, "dist", `devthink-firefox-${packagejson.version}.xpi`), Buffer.from(xpibuilt.archive.bytes));
923
- const lintercheck = xpipack.xpipacklintercheck({ markers: xpibuilt.lintermarkers, budget: xpipack.xpilinterbudget });
924
- if (!lintercheck.ok) throw new Error(lintercheck.reason ?? "The firefox xpi failed the addons linter budget.");
925
-
926
- /** The safari target of the 1.1.86 browser coverage family: the build embeds the chromium extension payload (the manifest, the background bundle, the page bundles and the assets) into the safari app extension wrapper through the safariskeleton module, and the wrapper ships beside the chromium zip and the firefox xpi with the bundle id, the entitlements and the minimal app shell the wrapper declares. */
927
- const safariskeleton = await import(join(root, "dist", "crossbrowser.js"));
928
- const safaripayload = [];
929
- safaripayload.push({
930
- name: "manifest.json",
931
- bytes: new Uint8Array(await readFile(join(extensiondist, "manifest.json"))),
932
- });
933
- for (const name of [
934
- "background.js",
935
- "popup.js",
936
- "sidepanel.js",
937
- "pagebridge.js",
938
- "offscreen.js",
939
- "transparencypage.js",
940
- "dashboardpage.js",
941
- "optionspage.js",
942
- ]) {
943
- try {
944
- safaripayload.push({ name, bytes: new Uint8Array(await readFile(join(extensiondist, name))) });
945
- } catch {
946
- /* optional */
947
- }
948
- }
949
- for (const asset of [
950
- "popup.html",
951
- "offscreen.html",
952
- "sandbox.html",
953
- "sidepanel.html",
954
- "transparencypage.html",
955
- "dashboardpage.html",
956
- "optionspage.html",
957
- "style.css",
958
- "icons/16.png",
959
- "icons/19.png",
960
- "icons/32.png",
961
- "icons/38.png",
962
- "icons/48.png",
963
- "icons/128.png",
964
- ]) {
965
- try {
966
- safaripayload.push({ name: asset, bytes: new Uint8Array(await readFile(join(extensiondist, asset))) });
967
- } catch {
968
- /* optional */
969
- }
970
- }
971
- const safaribuilt = safariskeleton.safariskeletonbuild({
972
- version: packagejson.version,
973
- bundleid: "com.wenathlan.devthink",
974
- extensionpayload: safaripayload,
975
- entitlements: ["com.apple.security.app-sandbox", "com.apple.security.network.client"],
976
- });
977
- await writeFile(
978
- join(root, "dist", `devthink-safari-${packagejson.version}.zip`),
979
- Buffer.from(safaribuilt.archive.bytes),
980
- );
981
-
982
- /** The vs code target of the 1.1.87 publishing pipeline family: the build reads the vsix manifest overlay (the vsix section of the root manifest.json), reuses the library esm build the webview imports, assembles the vsix through the vsixpack assembler with the extension manifest, the webview page and the esm bundle, runs the marketplace metadata check over the package fields and writes the artifact beside the browser builds; the package declares no telemetry and no network default, and no vendor marketplace url appears anywhere in the archive. */
983
- const vsixpack = await import(join(root, "dist", "pack.js"));
984
- const vsixmanifestsource = sourcemanifest.vsix;
985
- if (vsixmanifestsource === undefined)
986
- throw new Error(
987
- "The root manifest carries the vsix overlay under the vsix key; a missing overlay never builds the vs code target.",
988
- );
989
- const vsixbuilt = vsixpack.vsixpackassemble({
990
- version: packagejson.version,
991
- esmbundlebytes: new Uint8Array(await readFile(join(root, "dist", "index.js"))),
992
- ...(typeof vsixmanifestsource.relaypath === "string" ? { relaypath: vsixmanifestsource.relaypath } : {}),
993
- });
994
- await writeFile(join(root, "dist", vsixbuilt.archive.name), Buffer.from(vsixbuilt.archive.bytes));
995
- const vsixmetadata = vsixpack.vsixmarketplacemetadatacheck({
996
- fields: vsixbuilt.manifestfields,
997
- manifesttext: vsixpack.vsixpackmanifest(vsixbuilt.manifestfields),
998
- });
999
- if (!vsixmetadata.ok) throw new Error(vsixmetadata.reason ?? "The vsix failed the marketplace metadata check.");
1000
- if (vsixbuilt.manifestfields.version !== sourcemanifest.version)
1001
- throw new Error(
1002
- "The vsix manifest carries the version the root manifest owns; the single version source never drifts between the package stamp and the root manifest.",
1003
- );
1004
-
1005
- /** The declarations zip of the 1.1.87 publishing pipeline family: the build packs every declaration file and declaration map into the version stamped archive the release attaches beside the bundles and the maven pom attaches with the declarations classifier. */
1006
- const declarationsname = `devthink-declarations-${packagejson.version}.zip`;
1007
- const declarationfiles = (await readdir(join(root, "dist")))
1008
- .filter((file) => file.endsWith(".d.ts") || file.endsWith(".d.ts.map"))
1009
- .sort();
1010
- if (declarationfiles.length === 0)
1011
- throw new Error(
1012
- "The declarations zip packs the declaration files for ide integration; an empty declaration set never ships.",
1013
- );
1014
- await rm(join(root, "dist", declarationsname), { force: true });
1015
- await execute("zip", ["-qr", declarationsname, ...declarationfiles], { cwd: join(root, "dist") });
1016
- emitted.push(declarationsname);
1017
-
1018
- /** The site zip of the 1.1.87 publishing pipeline family: the build packs the hashed static site (the chatbridge assets and the immutable cache header configuration) into the version stamped archive the release attaches beside the extension artifacts for the site channel. */
1019
- const sitename = `devthink-site-${packagejson.version}.zip`;
1020
- await rm(join(root, "dist", sitename), { force: true });
1021
- await execute("zip", ["-qr", `../${sitename}`, "."], { cwd: sitedist });
1022
-
1023
- /** The artifact manifest of the 1.1.87 publishing pipeline family: the build records every artifact it produced — every dist bundle, the declarations zip and the extension artifacts — with its name, size, sha256 checksum and publishing channels; the manifest carries no timestamp so the same artifact set renders byte identical on every run, and the verify workflow asserts the built set matches it exactly. */
1024
- const artifactmanifestmodule = await import(join(root, "dist", "pack.js"));
1025
- const releaseartifacts = [];
1026
- for (const bundle of [...emitted]) {
1027
- if (bundle.startsWith("site/"))
1028
- continue; /* the static site assets carry their own manifest, not the artifact manifest of the publishing pipeline */
1029
- const bytes = await readFile(join(root, "dist", bundle));
1030
- releaseartifacts.push({
1031
- name: `dist/${bundle}`,
1032
- size: bytes.length,
1033
- checksum: createHash("sha256").update(bytes).digest("hex"),
1034
- });
1035
- }
1036
- for (const artifact of [
1037
- `devthink${packagejson.version}.zip`,
1038
- `devthink-firefox-${packagejson.version}.xpi`,
1039
- `devthink-safari-${packagejson.version}.zip`,
1040
- vsixbuilt.archive.name,
1041
- sitename,
1042
- ]) {
1043
- const bytes = await readFile(join(root, "dist", artifact));
1044
- releaseartifacts.push({
1045
- name: `dist/${artifact}`,
1046
- size: bytes.length,
1047
- checksum: createHash("sha256").update(bytes).digest("hex"),
1048
- });
1049
- }
1050
- const localmanifest = artifactmanifestmodule.artifactmanifestof({
1051
- version: packagejson.version,
1052
- artifacts: releaseartifacts,
1053
- });
1054
- await writeFile(
1055
- join(root, "dist", "artifactmanifest.json"),
1056
- artifactmanifestmodule.artifactmanifesttext(localmanifest),
1057
- "utf8",
1058
- );
1059
- emitted.push("artifactmanifest.json");
1060
-
1061
- /** The checksums file of every dist target: one sha256 line per emitted bundle and one line for the published manifest identity key, so a consumer verifies every mode file against the build that shipped it while the identity pin rides the generated artifact instead of a hard coded constant; the file is written after every artifact exists so the declarations zip and the artifact manifest join the covered set. */
1062
- const checksums = [];
1063
- for (const bundle of emitted.sort()) {
1064
- const digest = createHash("sha256")
1065
- .update(await readFile(join(root, "dist", bundle)))
1066
- .digest("hex");
1067
- checksums.push(`${digest} ${bundle}`);
1068
- }
1069
- const identitymanifest = JSON.parse(await readFile(join(root, "web/manifest.json"), "utf8"));
1070
- const identitykey = Buffer.from(String(identitymanifest.key), "base64");
1071
- checksums.push(`${createHash("sha256").update(identitykey).digest("hex")} manifest.json key`);
1072
- await writeFile(join(root, "dist", "checksums.txt"), `${checksums.join("\n")}\n`, "utf8");
1073
-
1074
- /** The apimap build check of the 1.1.86 browser coverage family: the build asserts every recorded api has a chromium and firefox mapping (a missing row fails the build before the cross browser artifacts ship), so a new api without a row never reaches the firefox or the safari build. */
1075
- const apimapmodule = await import(join(root, "dist", "crossbrowser.js"));
1076
- const apireport = apimapmodule.apimapunmapped(apimapmodule.apimapentries());
1077
- if (apireport.failed) throw new Error(apireport.reason);
1078
- console.log(
1079
- `Devthink targets built: ${targets.map((target) => `${target.runtime}=${targetoutput(target)}`).join(", ")} beside the esm and cjs core, the neutral browser bundle, the per module entries, the cli and the headless library entry.`,
1080
- );
1081
- console.log(
1082
- `Bundle size accounting: ${bundleaccounting.map((entry) => `${entry.target}=${entry.bytes}/${entry.budget}b`).join(", ")}.`,
1083
- );
1084
- console.log(`Checksums written for ${emitted.length} dist bundles into dist/checksums.txt.`);
1085
- console.log(
1086
- `Publishing pipeline artifacts: dist/${vsixbuilt.archive.name} (${vsixbuilt.entries.length} entries, ${vsixbuilt.archive.bytes.length} bytes), dist/${declarationsname} (${releaseartifacts.filter((entry) => entry.name === `dist/${declarationsname}`).length > 0 ? "declared" : "staged"}), dist/${sitename} and dist/artifactmanifest.json (${localmanifest.artifacts.length} artifacts with names, sizes, checksums and channels).`,
1087
- );
1088
- console.log(
1089
- `Static site built to dist/site with hashed file names: ${siteaccounting.map((entry) => `${entry.file}=${entry.bytes}b`).join(", ")} of ${sitebytes} total bytes and the immutable cache header configuration in dist/site/_headers.`,
1090
- );
1091
- console.log(
1092
- `Cross browser artifacts: dist/devthink-firefox-${packagejson.version}.xpi (${xpibuilt.entries.length} entries, ${xpibuilt.archive.bytes.length} bytes) and dist/devthink-safari-${packagejson.version}.zip (${safaribuilt.projectfiles.length} project files + ${safaripayload.length} payload entries, ${safaribuilt.archive.bytes.length} bytes).`,
1093
- );
1094
- console.log(
1095
- `Apimap build check: ${apireport.rows.length} rows, ${apireport.missingchromium.length} chromium missing, ${apireport.missingfirefox.length} firefox missing, ${apireport.missingsafari.length} safari missing.`,
1096
- );
1097
-
1098
- /** The flat npm package staging of the 2.0.3 organization pass: the library surface the exports map resolves ships at the package root — one file per correlated domain, no dist folder, no path nested past two directories — with the data groups (caps, schemas, fixtures) beside it, the umd example page, the extension manifest the cli manifest command reads, and the package scoped checksums covering exactly the shipped set. The source maps stay behind (the dist build and the release assets keep them — the published package rides lean with the js and the declarations), and the generated package.json mirrors the repository manifest with the entry paths remapped to the root, so the published tarball opens flat and organized the same way the repository sources stay; the release workflow packs the staged tree (`npm pack ./distpackage`) on the github build machines and the repository keeps no build output of its own. */
1099
- const stage = join(root, "distpackage");
1100
- await rm(stage, { recursive: true, force: true });
1101
- await mkdir(stage, { recursive: true });
1102
- const stagerootfiles = [
1103
- "index.js",
1104
- "index.cjs",
1105
- "index.neutral.js",
1106
- "devthink.umd.js",
1107
- "policy.js",
1108
- "protocol.js",
1109
- "memory.js",
1110
- "progress.js",
1111
- "hardening.js",
1112
- "dashdone.js",
1113
- "node.cjs",
1114
- "bun.js",
1115
- "deno.js",
1116
- "cli.js",
1117
- "headless.js",
1118
- "mcp.js",
1119
- "bridge.js",
1120
- "companion.js",
1121
- "nativehost.template.json",
1122
- "pack.js",
1123
- "http.js",
1124
- "server.js",
1125
- "crossbrowser.js",
1126
- "gallery.json",
1127
- "umd-example.html",
1128
- ];
1129
- const stagedeclarations = [
1130
- "index",
1131
- "policy",
1132
- "protocol",
1133
- "memory",
1134
- "progress",
1135
- "hardening",
1136
- "dashdone",
1137
- "cli",
1138
- "headless",
1139
- "mcp",
1140
- "bridge",
1141
- "pack",
1142
- "crossbrowser",
1143
- "http",
1144
- "server",
1145
- "umd",
1146
- "node",
1147
- "bun",
1148
- "deno",
1149
- "companion",
1150
- ];
1151
- for (const file of stagerootfiles) await writeFile(join(stage, file), await readFile(join(root, "dist", file)));
1152
- for (const name of stagedeclarations) {
1153
- await writeFile(join(stage, `${name}.d.ts`), await readFile(join(root, "dist", `${name}.d.ts`)));
1154
- await writeFile(join(stage, `${name}.d.ts.map`), await readFile(join(root, "dist", `${name}.d.ts.map`)));
1155
- }
1156
- for (const group of ["caps", "schemas", "fixtures"]) {
1157
- await mkdir(join(stage, group), { recursive: true });
1158
- const entries = [];
1159
- const walk = async (relative) => {
1160
- for (const entry of await readdir(join(root, "dist", group, relative), { withFileTypes: true })) {
1161
- const child = relative === "" ? entry.name : `${relative}/${entry.name}`;
1162
- if (entry.isDirectory()) await walk(child);
1163
- else entries.push(child);
1164
- }
1165
- };
1166
- await walk("");
1167
- for (const entry of entries.sort()) {
1168
- const parent = join(stage, group, entry.split("/").slice(0, -1).join("/"));
1169
- await mkdir(parent, { recursive: true });
1170
- await writeFile(join(stage, group, entry), await readFile(join(root, "dist", group, entry)));
1171
- }
1172
- }
1173
- for (const file of ["README.md", "LICENSE", "CHANGELOG.md"])
1174
- await writeFile(join(stage, file), await readFile(join(root, file)));
1175
- await writeFile(join(stage, "manifest.json"), await readFile(join(root, "web", "manifest.json")));
1176
-
1177
- /** The staged package manifest: the repository package.json is the single source of truth, the build derives the published manifest from it — the entry paths remap from the dist folder to the package root, the repository machinery (files allowlist, scripts, devDependencies, the packageManager pin) drops out, and every field a registry consumer reads rides verbatim — so the two manifests never drift. */
1178
- const remappaths = (node) => {
1179
- if (typeof node === "string") return node.replace(/^\.\/dist\//, "./");
1180
- if (Array.isArray(node)) return node.map(remappaths);
1181
- if (node !== null && typeof node === "object") {
1182
- const out = {};
1183
- for (const [key, value] of Object.entries(node)) out[key] = remappaths(value);
1184
- return out;
1185
- }
1186
- return node;
1187
- };
1188
- const stagedpackage = {
1189
- name: packagejson.name,
1190
- version: packagejson.version,
1191
- description: packagejson.description,
1192
- type: packagejson.type,
1193
- license: packagejson.license,
1194
- author: packagejson.author,
1195
- homepage: packagejson.homepage,
1196
- repository: packagejson.repository,
1197
- bugs: packagejson.bugs,
1198
- keywords: packagejson.keywords,
1199
- /** the runtime dependency set rides the staged manifest verbatim: the server subpath resolves @prisma/client and the libsql adapter at the first database touch, so a consumer that exercises the persistence path finds every runtime dependency installed with the package — the repository machinery (scripts, devDependencies, the packageManager pin) drops out, the runtime contract never does */
1200
- dependencies: packagejson.dependencies,
1201
- engines: packagejson.engines,
1202
- sideEffects: false,
1203
- publishConfig: packagejson.publishConfig,
1204
- main: "./index.js",
1205
- types: "./index.d.ts",
1206
- bin: { devthink: "./cli.js" },
1207
- exports: remappaths(packagejson.exports),
1208
- };
1209
- await writeFile(join(stage, "package.json"), `${JSON.stringify(stagedpackage, null, 2)}\n`, "utf8");
1210
-
1211
- /** The package scoped checksums: one sha256 line per staged file beside the identity pin of the manifest key, so a consumer verifies every file of the installed package against the build that shipped it — the minified variants and the release archives stay covered by the full dist checksums of the release assets, the package set carries its own closed record. */
1212
- const stagechecksums = [];
1213
- const collectstaged = async (relative) => {
1214
- const found = [];
1215
- for (const entry of await readdir(join(stage, relative), { withFileTypes: true })) {
1216
- const child = relative === "" ? entry.name : `${relative}/${entry.name}`;
1217
- if (entry.isDirectory()) found.push(...(await collectstaged(child)));
1218
- else found.push(child);
1219
- }
1220
- return found;
1221
- };
1222
- const stagedfiles = (await collectstaged("")).filter((file) => file !== "checksums.txt").sort();
1223
- for (const file of stagedfiles) {
1224
- stagechecksums.push(
1225
- `${createHash("sha256")
1226
- .update(await readFile(join(stage, file)))
1227
- .digest("hex")} ${file}`,
1228
- );
1229
- }
1230
- stagechecksums.push(`${createHash("sha256").update(identitykey).digest("hex")} manifest.json key`);
1231
- await writeFile(join(stage, "checksums.txt"), `${stagechecksums.join("\n")}\n`, "utf8");
1232
-
1233
- /** The flat layout gates of the staging: no staged path nests past two directories (a folder inside a folder inside a folder never ships), every exports target of the staged manifest resolves to a staged file, and the staged manifest parses with the repository name and version — the organized contract the release lanes assert again over the packed tarball. */
1234
- for (const file of stagedfiles) {
1235
- if (file.split("/").length > 3)
1236
- throw new Error(
1237
- `The staged package file ${file} nests past two directories; the npm package stays flat at the root with grouped data beside it.`,
1238
- );
1239
- }
1240
- const exporttargets = [];
1241
- const collecttargets = (node) => {
1242
- if (typeof node === "string") exporttargets.push(node);
1243
- else if (Array.isArray(node)) node.forEach(collecttargets);
1244
- else if (node !== null && typeof node === "object") Object.values(node).forEach(collecttargets);
1245
- };
1246
- collecttargets(stagedpackage.exports);
1247
- for (const target of [...new Set(exporttargets)]) {
1248
- try {
1249
- await readFile(join(stage, target.replace(/^\.\//, "")));
1250
- } catch {
1251
- throw new Error(
1252
- `The staged exports target ${target} does not resolve to a staged file; the package manifest and the staged set drifted.`,
1253
- );
1254
- }
1255
- }
1256
- if (stagedpackage.bin.devthink !== "./cli.js")
1257
- throw new Error("The staged bin entry resolves the cli at the package root.");
1258
- console.log(
1259
- `Flat npm package staged: distpackage of ${stagedfiles.length + 1} files (${stagerootfiles.length} root surface files, ${stagedeclarations.length * 2} declaration files, ${stagedfiles.filter((file) => file.startsWith("caps/") || file.startsWith("schemas/") || file.startsWith("fixtures/")).length} grouped data files) with the package scoped checksums and the root manifest — pack it with npm pack ./distpackage.`,
1260
- );