@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,461 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ authrefusedmessage,
4
+ capturecode,
5
+ checkallowlist,
6
+ defaultchallengelifetimems,
7
+ defaultpairinglifetimems,
8
+ defaulttokenlifetimems,
9
+ exchangebridgepairing,
10
+ expiretokens,
11
+ grantallowlistentry,
12
+ issuechallenge,
13
+ issuepairingcode,
14
+ issuetoken,
15
+ livetokensof,
16
+ mintbridgepairing,
17
+ oauthflowof,
18
+ pairingcountdown,
19
+ parsetokens,
20
+ redeempairingcode,
21
+ revokeallsessions,
22
+ revokeclient,
23
+ scopecheck,
24
+ tokenhashof,
25
+ tokenhashprefix,
26
+ tokenscopedkey,
27
+ verifyauth,
28
+ verifytoken,
29
+ } from "../auth.js";
30
+ import type { allowlistentry, clientidentity, sessiontoken } from "../types.js";
31
+
32
+ const now = 1_800_000_000_000;
33
+
34
+ /** Builds one allowlist fixture. */
35
+ function entry(fingerprint: string, namespaces: allowlistentry["namespaces"]): allowlistentry {
36
+ return {
37
+ fingerprint,
38
+ displayname: `Client ${fingerprint.slice(0, 4)}`,
39
+ namespaces,
40
+ grantedat: now - 1_000,
41
+ history: [{ at: now - 1_000, actor: "user", change: "Granted." }],
42
+ };
43
+ }
44
+
45
+ /** Builds one client identity fixture. */
46
+ function client(fingerprint: string): clientidentity {
47
+ return { fingerprint, displayname: `Client ${fingerprint.slice(0, 4)}` };
48
+ }
49
+
50
+ describe("torture: pairing code lifecycle", () => {
51
+ it("filters unknown scopes, keeps user lifetimes and generates dt codes", () => {
52
+ const code = issuepairingcode({ now, scopes: ["browser", "memory", "carrierpigeon" as never] });
53
+ expect(code.scopes).toEqual(["browser", "memory"]);
54
+ expect(code.code).toMatch(/^DT-[A-Z0-9]{8}$/);
55
+ expect(code.expiresat).toBe(now + defaultpairinglifetimems);
56
+ expect(issuepairingcode({ now, scopes: ["browser"], lifetime: 1_000 }).expiresat).toBe(now + 1_000);
57
+ expect(issuepairingcode({ now, scopes: [], lifetime: 0 }).expiresat).toBe(now);
58
+ expect(issuepairingcode({ now, scopes: ["browser"], code: "DT-CUSTOM" }).code).toBe("DT-CUSTOM");
59
+ expect(issuepairingcode({ now, scopes: ["browser"], code: "not-a-code" }).code).toBe("not-a-code");
60
+ });
61
+
62
+ it("two generated codes never collide in a large batch", () => {
63
+ const codes = new Set<string>();
64
+ for (let index = 0; index < 200; index += 1) codes.add(issuepairingcode({ now, scopes: ["browser"] }).code);
65
+ expect(codes.size).toBe(200);
66
+ });
67
+
68
+ it("redeems each code exactly once with the expiry tick exclusive", () => {
69
+ const code = issuepairingcode({ now, scopes: ["browser"], code: "DT-ONCE", lifetime: 1_000 });
70
+ const first = redeempairingcode({ codes: [code], code: "DT-ONCE", now: now + 500 });
71
+ expect(first.code?.usedat).toBe(now + 500);
72
+ const second = redeempairingcode({ codes: [first.code!], code: "DT-ONCE", now: now + 600 });
73
+ expect(second.code).toBeUndefined();
74
+ expect(second.reason).toMatch(/already used once/i);
75
+ const late = issuepairingcode({ now, scopes: ["browser"], code: "DT-LATE", lifetime: 1_000 });
76
+ expect(redeempairingcode({ codes: [late], code: "DT-LATE", now: now + 999 }).code).toBeDefined();
77
+ expect(redeempairingcode({ codes: [late], code: "DT-LATE", now: now + 1_000 }).reason).toMatch(/expired/i);
78
+ expect(redeempairingcode({ codes: [late], code: "DT-OTHER", now: now + 1 }).reason).toBe(authrefusedmessage);
79
+ expect(redeempairingcode({ codes: [], code: "DT-ONCE", now }).reason).toBe(authrefusedmessage);
80
+ });
81
+
82
+ it("code comparison stays exact against lookalike and malformed codes", () => {
83
+ const code = issuepairingcode({ now, scopes: ["browser"], code: "DT-AB12CD34" });
84
+ for (const lookalike of [
85
+ "dt-ab12cd34",
86
+ "DT-AB12CD35",
87
+ " DT-AB12CD34",
88
+ "DT-AB12CD34 ",
89
+ "DT-ABl2CD34",
90
+ "DT-AB12CD3",
91
+ ]) {
92
+ expect(redeempairingcode({ codes: [code], code: lookalike, now }).code).toBeUndefined();
93
+ }
94
+ });
95
+ });
96
+
97
+ describe("torture: session token hashing and verification", () => {
98
+ it("token hashes never carry the raw value and stay deterministic", async () => {
99
+ const hash = await tokenhashof("raw-token-value");
100
+ expect(hash.startsWith(tokenhashprefix)).toBe(true);
101
+ expect(hash).not.toContain("raw-token-value");
102
+ expect(await tokenhashof("raw-token-value")).toBe(hash);
103
+ expect(await tokenhashof("raw-token-valuE")).not.toBe(hash);
104
+ const empty = await tokenhashof("");
105
+ expect(empty.startsWith(tokenhashprefix)).toBe(true);
106
+ const unicode = await tokenhashof("こんにちは 🌍");
107
+ expect(unicode.startsWith(tokenhashprefix)).toBe(true);
108
+ const long = await tokenhashof("x".repeat(1_000_000));
109
+ expect(long.startsWith(tokenhashprefix)).toBe(true);
110
+ expect(await tokenhashof("a")).not.toBe(await tokenhashof("b"));
111
+ });
112
+
113
+ it("verifies only stored, unrevoked, unexpired tokens with fixed refusal messages", async () => {
114
+ const issued = await issuetoken({ clientid: "client1", scopes: ["browser"], now, lifetime: 1_000, raw: "raw-one" });
115
+ const wrong = await verifytoken({ tokens: [issued.token], raw: "raw-two", now: now + 1 });
116
+ expect(wrong.reason).toBe(authrefusedmessage);
117
+ const atlimit = await verifytoken({ tokens: [issued.token], raw: "raw-one", now: now + 1_000 });
118
+ expect(atlimit.token).toBeUndefined();
119
+ expect(atlimit.reason).toBe(authrefusedmessage);
120
+ const before = await verifytoken({ tokens: [issued.token], raw: "raw-one", now: now + 999 });
121
+ expect(before.token?.clientid).toBe("client1");
122
+ const revoked = revokeclient([issued.token], "client1", now + 1);
123
+ const afterrevoke = await verifytoken({ tokens: revoked, raw: "raw-one", now: now + 2 });
124
+ expect(afterrevoke.reason).toBe(authrefusedmessage);
125
+ const otherrevoked = revokeclient([issued.token], "client2", now + 1);
126
+ expect((await verifytoken({ tokens: otherrevoked, raw: "raw-one", now: now + 2 })).token?.clientid).toBe("client1");
127
+ });
128
+
129
+ it("revocation stays once and the audit record survives", async () => {
130
+ const issued = await issuetoken({ clientid: "client1", scopes: ["browser"], now, raw: "raw-one" });
131
+ const revokedonce = revokeclient([issued.token], "client1", now + 1);
132
+ const revokedtwice = revokeclient(revokedonce, "client1", now + 2);
133
+ expect(revokedtwice[0]?.revokedat).toBe(now + 1);
134
+ });
135
+
136
+ it("expiry sweeps exactly the unrevoked past their lifetime", async () => {
137
+ const live = (await issuetoken({ clientid: "c1", scopes: ["browser"], now, lifetime: 10_000, raw: "raw-live" }))
138
+ .token;
139
+ const dead = (await issuetoken({ clientid: "c2", scopes: ["browser"], now, lifetime: 1_000, raw: "raw-dead" }))
140
+ .token;
141
+ const revoked = (
142
+ await issuetoken({ clientid: "c3", scopes: ["browser"], now, lifetime: 1_000, raw: "raw-revoked" })
143
+ ).token;
144
+ const revokedrecord = revokeclient([revoked], "c3", now);
145
+ const swept = expiretokens({ tokens: [live, dead, ...revokedrecord], now: now + 1_001 });
146
+ expect(swept.expired.map((token) => token.clientid)).toEqual(["c2"]);
147
+ expect(swept.live.map((token) => token.clientid).sort()).toEqual(["c1", "c3"]);
148
+ const atlimit = expiretokens({ tokens: [dead], now: now + 1_000 });
149
+ expect(atlimit.expired.map((token) => token.clientid)).toEqual(["c2"]);
150
+ });
151
+
152
+ it("scope checks fail fast without a namespace and refuse ungranted scopes", async () => {
153
+ const token = (await issuetoken({ clientid: "c1", scopes: ["browser"], now, raw: "raw" })).token;
154
+ expect(scopecheck(undefined, undefined).fast).toBe(true);
155
+ expect(scopecheck(token, undefined).fast).toBe(true);
156
+ expect(scopecheck(undefined, "browser").reason).toMatch(/no verified session token/i);
157
+ expect(scopecheck(token, "browser").allowed).toBe(true);
158
+ expect(scopecheck(token, "memory").reason).toMatch(/grants no memory tools/i);
159
+ });
160
+ });
161
+
162
+ describe("torture: auth challenges and the handshake", () => {
163
+ it("challenges carry single use nonces with user lifetimes", () => {
164
+ const challenge = issuechallenge({ method: "token", now });
165
+ expect(challenge.nonce).toMatch(/^[0-9a-f-]{36}$/);
166
+ expect(challenge.expiresat).toBe(now + defaultchallengelifetimems);
167
+ expect(issuechallenge({ method: "token", now, lifetime: 1_000 }).expiresat).toBe(now + 1_000);
168
+ expect(issuechallenge({ method: "token", now, nonce: "fixed" }).nonce).toBe("fixed");
169
+ const nonces = new Set<string>();
170
+ for (let index = 0; index < 100; index += 1)
171
+ nonces.add(issuechallenge({ method: "token", now: now + index }).nonce);
172
+ expect(nonces.size).toBe(100);
173
+ });
174
+
175
+ it("the handshake refuses wrong nonces, expired challenges and bad tokens", async () => {
176
+ const challenge = issuechallenge({ method: "token", now, lifetime: 1_000, nonce: "nonce-1" });
177
+ const issued = await issuetoken({ clientid: "client1", scopes: ["browser"], now, raw: "raw-token" });
178
+ const wrongnonce = await verifyauth({
179
+ challenge,
180
+ nonce: "nonce-2",
181
+ tokens: [issued.token],
182
+ rawtoken: "raw-token",
183
+ now: now + 1,
184
+ });
185
+ expect(wrongnonce.reason).toBe(authrefusedmessage);
186
+ const late = await verifyauth({
187
+ challenge,
188
+ nonce: "nonce-1",
189
+ tokens: [issued.token],
190
+ rawtoken: "raw-token",
191
+ now: now + 1_000,
192
+ });
193
+ expect(late.reason).toMatch(/challenge expired/i);
194
+ const before = await verifyauth({
195
+ challenge,
196
+ nonce: "nonce-1",
197
+ tokens: [issued.token],
198
+ rawtoken: "raw-token",
199
+ now: now + 999,
200
+ });
201
+ expect(before.verified).toBe(true);
202
+ expect(before.clientid).toBe("client1");
203
+ const badtoken = await verifyauth({
204
+ challenge,
205
+ nonce: "nonce-1",
206
+ tokens: [issued.token],
207
+ rawtoken: "wrong",
208
+ now: now + 1,
209
+ });
210
+ expect(badtoken.reason).toBe(authrefusedmessage);
211
+ const emptynonce = await verifyauth({
212
+ challenge,
213
+ nonce: "",
214
+ tokens: [issued.token],
215
+ rawtoken: "raw-token",
216
+ now: now + 1,
217
+ });
218
+ expect(emptynonce.reason).toBe(authrefusedmessage);
219
+ });
220
+ });
221
+
222
+ describe("torture: allowlist discipline", () => {
223
+ it("refuses unknown fingerprints and ungranted namespaces with exact reasons", () => {
224
+ const entries = [entry("fp123", ["browser", "memory"])];
225
+ expect(checkallowlist({ entries, fingerprint: "fp123" }).allowed).toBe(true);
226
+ expect(checkallowlist({ entries, fingerprint: "fp123", namespace: "browser" }).allowed).toBe(true);
227
+ expect(checkallowlist({ entries, fingerprint: "fp123", namespace: "memory" }).allowed).toBe(true);
228
+ const refused = checkallowlist({ entries, fingerprint: "fp123", namespace: "system" });
229
+ expect(refused.allowed).toBe(false);
230
+ expect(refused.reason).toMatch(/grants no system tools/i);
231
+ const unknown = checkallowlist({ entries, fingerprint: "fp999" });
232
+ expect(unknown.allowed).toBe(false);
233
+ expect(unknown.reason).toMatch(/not on the allowlist/i);
234
+ expect(checkallowlist({ entries: [], fingerprint: "fp123" }).allowed).toBe(false);
235
+ expect(checkallowlist({ entries, fingerprint: "" }).allowed).toBe(false);
236
+ });
237
+
238
+ it("fingerprint comparison stays exact against lookalikes", () => {
239
+ const entries = [entry("fp123", ["browser"])];
240
+ for (const lookalike of ["FP123", "fp12", "fp1234", " fp123", "fp123 "]) {
241
+ expect(checkallowlist({ entries, fingerprint: lookalike }).allowed).toBe(false);
242
+ }
243
+ });
244
+
245
+ it("grants record scope history and rescope keeps the trail", () => {
246
+ const granted = grantallowlistentry({
247
+ entries: [],
248
+ identity: client("fp1"),
249
+ namespaces: ["browser"],
250
+ actor: "user",
251
+ now,
252
+ });
253
+ expect(granted[0]?.namespaces).toEqual(["browser"]);
254
+ expect(granted[0]?.history.length).toBe(1);
255
+ expect(granted[0]?.history[0]?.change).toMatch(/Granted the browser namespaces/i);
256
+ const rescoped = grantallowlistentry({
257
+ entries: granted,
258
+ identity: client("fp1"),
259
+ namespaces: ["browser", "memory"],
260
+ actor: "user",
261
+ now: now + 1,
262
+ });
263
+ expect(rescoped[0]?.namespaces).toEqual(["browser", "memory"]);
264
+ expect(rescoped[0]?.history.length).toBe(2);
265
+ expect(rescoped[0]?.history[0]?.change).toMatch(/Rescoped to browser, memory namespaces/i);
266
+ const unknownscopes = grantallowlistentry({
267
+ entries: [],
268
+ identity: client("fp2"),
269
+ namespaces: ["browser", "nope" as never],
270
+ actor: "user",
271
+ now,
272
+ });
273
+ expect(unknownscopes[0]?.namespaces).toEqual(["browser"]);
274
+ const empty = grantallowlistentry({ entries: [], identity: client("fp3"), namespaces: [], actor: "user", now });
275
+ expect(empty[0]?.namespaces).toEqual([]);
276
+ expect(empty[0]?.history[0]?.change).toMatch(/Granted the no namespaces/i);
277
+ });
278
+ });
279
+
280
+ describe("torture: oauth flow parsing and code capture", () => {
281
+ it("parses only well formed oauth flows", () => {
282
+ expect(oauthflowof(null)).toBeUndefined();
283
+ expect(oauthflowof(undefined)).toBeUndefined();
284
+ expect(oauthflowof("string")).toBeUndefined();
285
+ expect(oauthflowof(42)).toBeUndefined();
286
+ expect(oauthflowof([])).toBeUndefined();
287
+ expect(oauthflowof({})).toBeUndefined();
288
+ const flow = oauthflowof({
289
+ provider: "acme",
290
+ authorizeurl: "https://acme.example/authorize",
291
+ tokenurl: "https://acme.example/token",
292
+ scopes: ["read"],
293
+ redirectorigin: "https://app.example",
294
+ });
295
+ expect(flow?.provider).toBe("acme");
296
+ expect(
297
+ oauthflowof({
298
+ provider: "",
299
+ authorizeurl: "https://acme.example/authorize",
300
+ tokenurl: "https://acme.example/token",
301
+ scopes: [],
302
+ redirectorigin: "https://app.example",
303
+ }),
304
+ ).toBeUndefined();
305
+ });
306
+
307
+ it("captures codes only for the exact state and redirect origin", () => {
308
+ const state = "xyz123";
309
+ expect(
310
+ capturecode("https://app.example/callback?code=the-code&state=xyz123", "https://app.example", state),
311
+ ).toEqual({ code: "the-code" });
312
+ expect(
313
+ capturecode("https://evil.example/callback?code=the-code&state=xyz123", "https://app.example", state).error,
314
+ ).toBeDefined();
315
+ expect(
316
+ capturecode("https://app.example/callback?code=the-code&state=other", "https://app.example", state).error,
317
+ ).toBeDefined();
318
+ expect(capturecode("https://app.example/callback?state=xyz123", "https://app.example", state).error).toBeDefined();
319
+ expect(
320
+ capturecode("https://app.example/callback?error=access_denied&state=xyz123", "https://app.example", state).error,
321
+ ).toMatch(/access_denied/i);
322
+ expect(capturecode("not a url", "https://app.example", state).error).toBeDefined();
323
+ expect(
324
+ capturecode("https://app.example/callback?code=x&state=xyz123", "https://app.example.evil.com", state).error,
325
+ ).toBeDefined();
326
+ });
327
+
328
+ it("parses token bodies conservatively", () => {
329
+ expect(
330
+ parsetokens('{"access_token":"a","refresh_token":"r","expires_in":3600,"scope":"read write"}'),
331
+ ).toMatchObject({ accesstoken: "a", refreshtoken: "r", expiresin: 3600, scopes: ["read", "write"] });
332
+ expect(parsetokens('{"access_token":"a"}')).toMatchObject({ accesstoken: "a" });
333
+ expect(parsetokens("{}")).toBeUndefined();
334
+ expect(parsetokens("not json")).toBeUndefined();
335
+ expect(parsetokens("[]")).toBeUndefined();
336
+ expect(parsetokens('{"expires_in":"not a number"}')).toBeUndefined();
337
+ expect(parsetokens('{"expires_in":0}')).toBeUndefined();
338
+ expect(parsetokens('{"expires_in":-5}')).toBeUndefined();
339
+ expect(parsetokens('{"expires_in":3600.5}')).toBeUndefined();
340
+ expect(parsetokens('{"scope":"read"}')).toBeUndefined();
341
+ });
342
+ });
343
+
344
+ describe("torture: bridge pairing and relay tokens", () => {
345
+ it("mints bridge pairings bound to their relay origin", () => {
346
+ const record = mintbridgepairing({ origin: "https://relay.example", now, code: "DT-BRIDGE1", lifetime: 60_000 });
347
+ expect(record.origin).toBe("https://relay.example");
348
+ expect(record.code.code).toBe("DT-BRIDGE1");
349
+ expect(record.code.scopes).toEqual(["browser"]);
350
+ expect(() => mintbridgepairing({ origin: " ", now })).toThrow(/relay origin/i);
351
+ expect(() => mintbridgepairing({ origin: "", now })).toThrow(/relay origin/i);
352
+ expect(mintbridgepairing({ origin: " https://relay.example ", now }).origin).toBe("https://relay.example");
353
+ });
354
+
355
+ it("the countdown reports the exact seconds left and the expired flag at the boundary", () => {
356
+ const record = mintbridgepairing({ origin: "https://relay.example", now, code: "DT-B1", lifetime: 90_000 });
357
+ expect(pairingcountdown(record, now).secondsleft).toBe(90);
358
+ expect(pairingcountdown(record, now).expired).toBe(false);
359
+ expect(pairingcountdown(record, now + 89_999).secondsleft).toBe(1);
360
+ expect(pairingcountdown(record, now + 90_000).expired).toBe(true);
361
+ expect(pairingcountdown(record, now + 120_000).secondsleft).toBe(0);
362
+ const used = { ...record, code: { ...record.code, usedat: now + 1 } };
363
+ expect(pairingcountdown(used, now + 2).expired).toBe(true);
364
+ });
365
+
366
+ it("exchanges bridge pairings once per code and refuses foreign origins", async () => {
367
+ const record = mintbridgepairing({ origin: "https://relay.example", now, code: "DT-X1", lifetime: 60_000 });
368
+ const first = await exchangebridgepairing({
369
+ records: [record],
370
+ origin: "https://relay.example",
371
+ code: "DT-X1",
372
+ sessionid: "sess1",
373
+ now: now + 1,
374
+ });
375
+ expect(first.record).toBeDefined();
376
+ expect(first.raw).toBeDefined();
377
+ expect(first.record?.hash.startsWith(tokenhashprefix)).toBe(true);
378
+ expect(first.used?.usedat).toBe(now + 1);
379
+ const threaded = [{ ...record, code: first.used! }];
380
+ const replay = await exchangebridgepairing({
381
+ records: threaded,
382
+ origin: "https://relay.example",
383
+ code: "DT-X1",
384
+ sessionid: "sess2",
385
+ now: now + 2,
386
+ });
387
+ expect(replay.record).toBeUndefined();
388
+ expect(replay.reason).toMatch(/already used once/i);
389
+ const foreign = await exchangebridgepairing({
390
+ records: [record],
391
+ origin: "https://other.example",
392
+ code: "DT-X1",
393
+ sessionid: "sess3",
394
+ now: now + 1,
395
+ });
396
+ expect(foreign.record).toBeUndefined();
397
+ const blankorigin = await exchangebridgepairing({
398
+ records: [record],
399
+ origin: " ",
400
+ code: "DT-X1",
401
+ sessionid: "sess4",
402
+ now,
403
+ });
404
+ expect(blankorigin.reason).toMatch(/relay origin/i);
405
+ const blanksession = await exchangebridgepairing({
406
+ records: [record],
407
+ origin: "https://relay.example",
408
+ code: "DT-X1",
409
+ sessionid: " ",
410
+ now,
411
+ });
412
+ expect(blanksession.reason).toMatch(/session/i);
413
+ const unknowncode = await exchangebridgepairing({
414
+ records: [record],
415
+ origin: "https://relay.example",
416
+ code: "DT-NOPE",
417
+ sessionid: "sess5",
418
+ now,
419
+ });
420
+ expect(unknowncode.reason).toMatch(/matches the typed code/i);
421
+ const late = await exchangebridgepairing({
422
+ records: [{ ...record, code: { ...record.code, expiresat: now + 1 } }],
423
+ origin: "https://relay.example",
424
+ code: "DT-X1",
425
+ sessionid: "sess6",
426
+ now: now + 1,
427
+ });
428
+ expect(late.reason).toMatch(/expired/i);
429
+ });
430
+
431
+ it("relay token storage keys scope per origin and revoke all kills every live token", () => {
432
+ expect(tokenscopedkey("https://relay.example")).toBe("bridgetokens:https://relay.example");
433
+ expect(tokenscopedkey(" https://relay.example ")).toBe("bridgetokens:https://relay.example");
434
+ expect(() => tokenscopedkey("")).toThrow(/relay origin/i);
435
+ const tokens = [
436
+ {
437
+ id: "tok1",
438
+ origin: "https://relay.example",
439
+ hash: "sha256:1",
440
+ sessionid: "s1",
441
+ issuedat: now,
442
+ expiresat: now + 1_000,
443
+ },
444
+ { id: "tok2", origin: "https://relay.example", hash: "sha256:2", sessionid: "s2", issuedat: now },
445
+ { id: "tok3", origin: "https://other.example", hash: "sha256:3", sessionid: "s3", issuedat: now },
446
+ ];
447
+ expect(livetokensof(tokens, "https://relay.example", now).length).toBe(2);
448
+ expect(livetokensof(tokens, "https://relay.example", now + 1_001).length).toBe(1);
449
+ const revoked = revokeallsessions(tokens, now + 1);
450
+ expect(livetokensof(revoked, "https://relay.example", now + 1)).toEqual([]);
451
+ expect(revoked.every((token) => token.revokedat === now + 1)).toBe(true);
452
+ });
453
+ });
454
+
455
+ describe("torture: token hash collision resistance over a batch", () => {
456
+ it("distinct raw tokens hash distinctly across ten thousand tokens", async () => {
457
+ const hashes = new Set<string>();
458
+ for (let index = 0; index < 10_000; index += 1) hashes.add(await tokenhashof(`raw-token-${index}`));
459
+ expect(hashes.size).toBe(10_000);
460
+ }, 30_000);
461
+ });