@raingor/pi-web-switch 0.3.1 → 0.3.3
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.
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-1-1785132166652.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-1-1785291744290.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-1-1785291756870.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-1-1785293871314.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-10-1785297139944.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-2-1785132240502.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-2-1785132249748.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-2-1785294992352.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-2-1785295104532.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-2-1785295132507.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-3-1785132387218.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-4-1785295319523.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-4-1785295349664.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-4-1785295399600.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-5-1785295450850.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-5-1785295487872.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-5-1785295562600.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-5-1785295761054.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-5-1785295840175.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-6-1785295915007.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-6-1785296069913.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-7-1785296128252.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-9-1785297091531.png +0 -0
- package/.pi/.pi-puppeteer/artifacts/screenshots/session-9-1785297116790.png +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ActorSafetyLists/9.5220.3721/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ActorSafetyLists/9.5220.3721/listdata.json +91946 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ActorSafetyLists/9.5220.3721/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/AmountExtractionHeuristicRegexes/4/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/AmountExtractionHeuristicRegexes/4/heuristic_regexes.binarypb +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/AmountExtractionHeuristicRegexes/4/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/BrowserMetrics/BrowserMetrics-6A6978EB-A861.pma +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/BrowserMetrics-spare.pma +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/CaptchaProviders/8.5419.4434/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/CaptchaProviders/8.5419.4434/captcha_providers.json +12 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/CaptchaProviders/8.5419.4434/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/CertificateRevocation/10680/LICENSE +27 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/CertificateRevocation/10680/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/CertificateRevocation/10680/crl-set +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/CertificateRevocation/10680/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ChromeFeatureState +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Consent To Send Stats +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Crowd Deny/2026.7.27.84/Preload Data +24078 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Crowd Deny/2026.7.27.84/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Crowd Deny/2026.7.27.84/manifest.json +6 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/.com.google.Chrome.Hbf1ld +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Account Web Data +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Account Web Data-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Affiliation Database +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Affiliation Database-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/AutofillAiModelCache/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/AutofillAiModelCache/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/AutofillAiModelCache/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/AutofillStrikeDatabase/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/AutofillStrikeDatabase/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/AutofillStrikeDatabase/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/BookmarkMergedSurfaceOrdering +2 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/BudgetDatabase/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/BudgetDatabase/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/BudgetDatabase/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/003827f5ea5ac9e9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/00473eec8b9b56d5_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/05a190c784b641e3_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/066df8884be8cf8d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/09627b0809e61f83_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/09b27ac23861b1d7_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/0fd9412b623e5a66_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/118fb0fa2505d3b2_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/1194588b7e2b20e9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/135a3004445f6f03_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/13f1e7131fe25bc6_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/15a2a5dccd304be7_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/17893ac60e1e94c2_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/19474b63539a4609_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/1a1ecc22219568db_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/1bb0006133ebefa3_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/1cfaeeed4b13a026_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/1e3cf5f23a4de58b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/20427e8d33888ba3_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/2258d238accf0bfa_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/228c3ad9d63adf48_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/2437d308abe15ac2_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/246f33a252b55bce_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/25862298f2999d7d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/262d9bff9f7f64bb_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/264aad789515de93_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/269ad8897b8e63d0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/2cd2c50a38161cab_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/2dd4db1365eeeac8_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/2eae0c639fd772df_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/30bb21d39b68bd84_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/31121aeb5ecffa5f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/32766be1527c8e9a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/328e80b1ed10d28d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/339dcbdde8b8ac4e_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/34439c1d4bfc8dad_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/381d5603ee3fd8b9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/38fbe92680d28074_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/393ce7bb08410970_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/3d74b8be0f86614f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/4016e35a2021656d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/409bcaf42c9c09b4_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/41888ebeeb475a59_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/4714ef11264459f5_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/479d2c4f7d4f3b38_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/4824ea24720f1a77_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/4a73d37e6e4ce922_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/4ecdd068adbdf42a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/54e6cc05346dd07c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/5505ea174dc90263_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/551679343b836505_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/573f6264a77df462_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/578926a5c39768ed_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/582a8182776d9380_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/5f3aa184b329759d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/6351cd107efed247_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/637516a46d1d8587_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/65e82d9cb58cbd6d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/66d24a1529d6dae0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/6995a7c799490d3d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/6d10426240c5ff67_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/6dc325603c897784_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/6fbab020dd731222_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/70fa542a6974378a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/72a8c452abd1b0e6_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/72df48e9888c822b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/73abcb8da3b9f73d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/741e8bcc257a64a9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/75df0820975fcdfa_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/7789e307be36811d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/79a161a9b2f4a11e_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/7afd0bc7f9852bf0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/7ce691ce1ceb3b8d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/7ef6780a186001ac_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/80402f00feb6a222_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/85b55ea0ca260643_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/86300205c03092e4_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/87c94a2eac816ea9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/880c25af3ab59187_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/8a7d0881c813aeec_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/8bad3b1bccd25517_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/8bd9377481fa7d43_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/8c1a58ce19e15d16_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/8e44e385f5911b07_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/90e39d8d065f6ac0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/93d81321f8c35eab_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/93e9945beb439414_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/944e08f273fccc44_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/95edd63493c49660_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/9789c4ee76bdb350_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/9a5911308e0f26ad_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/9a6cdf8f5c03aee5_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/9aeb4a3d1a607b07_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/9aff0e1ff1742ee0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/9bdabd2703f2e3ba_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/9d4d11ac8a8e264f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/9dd169e672e31961_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/9e72442bf8821115_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/a330965ccf5564ca_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/a3a3c6351a3f2f42_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/a818977d416f829d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/aa452720fc9d634f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/ac06b722aea9fcb8_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/aca3b8cf1f98e0bb_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/add439b7aae9334a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/b0113626d8ba968d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/ba97b9d927729aa7_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/bfcb9d225e4f11a8_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/c364377515d768e0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/c4376485fa897eca_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/c5b74f49505d3ecc_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/c7e10eb03f6d5cc2_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/c91efba145098b4c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/caecbbde37e3c866_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/cdddd64b252697fd_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/d3efa3ab37db7796_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/d6289ab00fe343c0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/d6be42432091d3ae_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/d7137f065f39412f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/d83db61c108fa34f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/d86cd6e1d99a8eaf_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/d97f1cfcc2edd001_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/de620df53673a107_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/de86892a6fa4e33b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/deb2cfe585bf96b0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/df93d6767095d5ea_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e1f8d8432edb7ea6_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e2e1058ec6a8e645_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e39ddd81e303f259_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e468a0fb6aa7fd4e_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e46f51470717ac3c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e4a531774ba47406_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e57a3a1fa60638e1_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e594d414c7f4d6a1_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e771a081db13a5b8_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e78c0ca6dbdafcd0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e8e671ad5c3fb2a8_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/e96e46c4d92b214f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/ea24ade36915d935_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/eaa70273ce58d3b3_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/eca41bfe04d1680d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/ed551f6d52fa6049_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/ee2190fb8068ea31_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/ee688f43baa25b4b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/f016dffa17337f95_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/f0707f1f113fa72c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/f12650f2be214ec8_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/f1887b75d61dbd90_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/f28900863f2e45b7_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/f4ee911c81881048_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/f557a2bca202c132_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/f61ed35c6c0b87d8_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/f6f1caefb3e18ade_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/fd695fa621d42490_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/Cache_Data/index-dir/the-real-index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/No_Vary_Search/journal.baj +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cache/No_Vary_Search/snapshot.baf +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/ClientCertificates/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/ClientCertificates/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/ClientCertificates/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/02b3a268866a7a0c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/0edbc65a5e505019_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/1051d2e224b9dd7a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/13cf7a1704163ded_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/16860cee78cb7179_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/17ea85d6fceb9229_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/1b6c1ba202c59494_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/1ebec9ea59157454_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/1ee2a6b57d251d09_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/2445a9737b996e2a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/2d10a08da99d6d96_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/325c8b935b677e9d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/454c49e6be2650e9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/4a34df5a255ad22e_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/4dfd5beef212627f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/5fe872a733479fff_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/66943b0755a2799d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/6a1839253b0d0b0a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/6e13894af36f3b79_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/786d3f1f49d31154_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/82f67f35254528ab_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/831e5a135cde918e_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/9965b2c8a33ec50d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/a15b1367fd75ef89_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/a7c7741248aeea07_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/a8e54c52a52541a9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/afa0d16c75997a6c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/b35d29b5b3583a88_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/bc55d051b6fd0744_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/bc8566d00f634dfb_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/c0be8aeb35882f7c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/c771807a395d295c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/c9058e9015c26dc7_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/d6804628a868f972_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/d7f211a5cec70609_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/d9c03164fc246389_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/e1011bf217f0ad7a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/efd85d739d006527_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/f5fe496bf55cd561_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/fc52f43646894a73_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/fc8954eb9445852d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/ff6de2f37189b29f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/js/index-dir/the-real-index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/wasm/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Code Cache/wasm/index-dir/the-real-index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cookies +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Cookies-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DIPS +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnGraphiteCache/data_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnGraphiteCache/data_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnGraphiteCache/data_2 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnGraphiteCache/data_3 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnGraphiteCache/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnWebGPUCache/data_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnWebGPUCache/data_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnWebGPUCache/data_2 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnWebGPUCache/data_3 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/DawnWebGPUCache/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension Rules/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension Rules/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension Rules/LOG +2 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension Rules/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension Scripts/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension Scripts/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension Scripts/LOG +2 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension Scripts/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension State/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension State/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension State/LOG +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension State/LOG.old +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Extension State/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Favicons +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Favicons-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Feature Engagement Tracker/AvailabilityDB/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Feature Engagement Tracker/AvailabilityDB/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Feature Engagement Tracker/AvailabilityDB/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Feature Engagement Tracker/EventDB/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Feature Engagement Tracker/EventDB/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Feature Engagement Tracker/EventDB/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GCM Store/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GCM Store/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GCM Store/LOG +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GCM Store/LOG.old +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GCM Store/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GPUCache/data_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GPUCache/data_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GPUCache/data_2 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GPUCache/data_3 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/GPUCache/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/History +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/History-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Local Storage/leveldb/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Local Storage/leveldb/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Local Storage/leveldb/LOG +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Local Storage/leveldb/LOG.old +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Local Storage/leveldb/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Login Data +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Login Data For Account +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Login Data For Account-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Login Data-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Network Action Predictor +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Network Action Predictor-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Network Persistent State +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/PersistentOriginTrials/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/PersistentOriginTrials/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/PersistentOriginTrials/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Preferences +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/PreferredApps +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/README +1 -0
- NEL +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Reporting and NEL-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Safe Browsing Cookies +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Safe Browsing Cookies-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Secure Preferences +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Segmentation Platform/SegmentInfoDB/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Segmentation Platform/SegmentInfoDB/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Segmentation Platform/SegmentInfoDB/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Segmentation Platform/SignalDB/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Segmentation Platform/SignalDB/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Segmentation Platform/SignalDB/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Segmentation Platform/SignalStorageConfigDB/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Segmentation Platform/SignalStorageConfigDB/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Segmentation Platform/SignalStorageConfigDB/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/ServerCertificate +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/ServerCertificate-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/09ed93488198f423_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/09ed93488198f423_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/0a30220965d74d1e_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/1300177f362c3a4a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/1300177f362c3a4a_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/27b9ccf88365ee26_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/2921673031323747_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/2921673031323747_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/35f037f406300022_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/35f037f406300022_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/3adbee165bfbf214_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/3c8f4750e95148d9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/3ce96b151d8c7cc1_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/3ce96b151d8c7cc1_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/483d56a1df05215a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/483d56a1df05215a_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/4d82f9d0e0941cce_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/4f42af3671d8c6c6_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/4f42af3671d8c6c6_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/5973e266a3773c26_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/5ad1663b68163b6f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/5b4c15b3725d9989_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/5b4c15b3725d9989_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/5c619235d9c64bc3_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/5c619235d9c64bc3_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/5e3af97b8dccb412_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/5e3af97b8dccb412_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/6e068fc05df362b0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/6e068fc05df362b0_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/755a4c7663f8dc02_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/755a4c7663f8dc02_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/7969561312c7823f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/7969561312c7823f_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/79ffa2733a735063_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/7f91cd3066f0019f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/7f91cd3066f0019f_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/7f97d42038a92017_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/7f97d42038a92017_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/820d43cc26b882ff_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/820d43cc26b882ff_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/88e95753fb6f9ea9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/8b469fd8bafad3b3_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/8b469fd8bafad3b3_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/900dd661df81967a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/9546d2c6dd38203c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/9546d2c6dd38203c_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/96d03a8b051613e1_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/96d03a8b051613e1_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/a0222364eb3781e3_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/a0222364eb3781e3_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/a524d2c2d64c3660_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/a524d2c2d64c3660_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/a78bfbcc1859fb76_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/a78bfbcc1859fb76_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/ac56d7aa94c8dd89_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/ac56d7aa94c8dd89_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/b5055aa76b1823e0_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/b5055aa76b1823e0_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/c0b18bd33c20f43c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/c0b18bd33c20f43c_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/c31100b4d5efc61d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/c31100b4d5efc61d_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/c62331d49f110533_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/c62331d49f110533_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/c7bb739419f12b43_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/cdf6536f6656df3e_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/cdf6536f6656df3e_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/d0e2620915e19afb_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/d0e2620915e19afb_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/d4313ca21cedf2ad_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/d4313ca21cedf2ad_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/d4cbe68232f52d97_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/d4cbe68232f52d97_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/d90bf1dbbf713592_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/d90bf1dbbf713592_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/df18dfd2a1ad9fb4_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/df18dfd2a1ad9fb4_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/e321e1f483bdf8f3_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/e321e1f483bdf8f3_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/e3cf32438c59b057_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/e3cf32438c59b057_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/e5d70b58b9ad133a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/e5d70b58b9ad133a_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/e6bebc23f041524b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/e6bebc23f041524b_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/ec2dbc36a8a707bf_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f0d3846d04ec57fa_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f0d3846d04ec57fa_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f41ecefc4e8c4d02_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f41ecefc4e8c4d02_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f641877808e85797_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f641877808e85797_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f792102a3adfa574_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f792102a3adfa574_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f87456efb50ead2b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f87456efb50ead2b_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f9c75818bcb88359_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/f9c75818bcb88359_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/fb21145c8e2675cb_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/fb21145c8e2675cb_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/56407312-0248-4546-be02-0d16e58a3522/index-dir/the-real-index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/d12381ba-daf3-4f23-9177-0ab3706f83a2/0281c98fa97441e5_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/d12381ba-daf3-4f23-9177-0ab3706f83a2/319cb45fc46cfd93_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/d12381ba-daf3-4f23-9177-0ab3706f83a2/685282d5721939ea_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/d12381ba-daf3-4f23-9177-0ab3706f83a2/8634e8d5eb849493_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/d12381ba-daf3-4f23-9177-0ab3706f83a2/c6ba0abf049dda93_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/d12381ba-daf3-4f23-9177-0ab3706f83a2/ff32d8eaf874a37b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/d12381ba-daf3-4f23-9177-0ab3706f83a2/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/d12381ba-daf3-4f23-9177-0ab3706f83a2/index-dir/the-real-index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/046fd1ef5e540ebf33332849ed99f4174f5bbafc/index.txt +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/024678dda211fa26_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/024678dda211fa26_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/037830b98bc2d51d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/037830b98bc2d51d_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/058b480bfb167f1d_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/058b480bfb167f1d_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/0655e9ff0425de79_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/0a59caba16743dc4_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/0a59caba16743dc4_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/136ff5c99b26781c_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/136ff5c99b26781c_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/17a566dc27c99ba4_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/17a566dc27c99ba4_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/21f0f05b2eceebc6_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/21f0f05b2eceebc6_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/258481d12e2d6b56_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/258481d12e2d6b56_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/384fffcb0c07b624_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/4d19946442d4bfbb_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/4db2b1b4ab66bab9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/4db2b1b4ab66bab9_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/51f125f305445381_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/51f125f305445381_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/5a8718d400d2f051_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/5a8718d400d2f051_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/61766447abeea15f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/61766447abeea15f_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/62151dfee245ae9b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/62151dfee245ae9b_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/696c1b7392b5c15a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/696c1b7392b5c15a_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/6c82448ab5b28377_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/6c82448ab5b28377_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/6d24995ced602e98_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/722befb3ecafcb3f_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/78c3660b76e7d93a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/78c3660b76e7d93a_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/798dd4b9b7d9d5c2_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/8595071248e29ee1_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/8595071248e29ee1_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/8b04b7c22f0fd1f9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/8b04b7c22f0fd1f9_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/8f520d5d41ee00bd_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/8f520d5d41ee00bd_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/901ff14ab13b28c6_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/901ff14ab13b28c6_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/a1d474dd0b6dd558_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/a1d474dd0b6dd558_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/a3011f5e2ef033c1_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/a3011f5e2ef033c1_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/a8b1cac4bc45ce95_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/a8b1cac4bc45ce95_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/ba58c86b9d2825b2_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/ba58c86b9d2825b2_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/c5d7b8f405d5e062_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/ca40ebe1bbd077ac_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/ca40ebe1bbd077ac_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/d0ab2b640b0a13e7_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/d0ab2b640b0a13e7_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/d0c3865f553ac976_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/d0c3865f553ac976_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/da45add0238a285a_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/da45add0238a285a_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/e4307c16470799da_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/e4307c16470799da_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/e7690ca8c63cad14_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/e7690ca8c63cad14_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/e7ba6f4de6562c89_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/e7ba6f4de6562c89_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/eb1bcf853b355e13_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/eb1bcf853b355e13_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/f32ce7214b1774ba_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/f32ce7214b1774ba_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/f62e4b05ee7d99f9_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/f62e4b05ee7d99f9_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/fc5ef4b63ae4bb4b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/3691c090-dad1-4066-a2d5-4462a09e7435/index-dir/the-real-index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/de96e545-808a-4ef3-b7b1-a72521505ad2/52158ab3c1d3fdde_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/de96e545-808a-4ef3-b7b1-a72521505ad2/9c3cfbb43286df23_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/de96e545-808a-4ef3-b7b1-a72521505ad2/b19f1b829e018cf2_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/de96e545-808a-4ef3-b7b1-a72521505ad2/b8ae4f867095f4fd_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/de96e545-808a-4ef3-b7b1-a72521505ad2/bdc920a7c2524c3b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/de96e545-808a-4ef3-b7b1-a72521505ad2/ea5f9af36eefd38b_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/de96e545-808a-4ef3-b7b1-a72521505ad2/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/de96e545-808a-4ef3-b7b1-a72521505ad2/index-dir/the-real-index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/CacheStorage/2f12120e9e05877e5620444e87e1a223bba7f923/index.txt +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/Database/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/Database/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/Database/LOG +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/Database/LOG.old +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/Database/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/ScriptCache/2cc80dabc69f58b6_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/ScriptCache/2cc80dabc69f58b6_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/ScriptCache/fa813c9ad67834ac_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/ScriptCache/fa813c9ad67834ac_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/ScriptCache/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Service Worker/ScriptCache/index-dir/the-real-index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Session Storage/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Session Storage/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Session Storage/LOG +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Session Storage/LOG.old +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Session Storage/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Sessions/Session_13429768577966436 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Sessions/Tabs_13429770684888237 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Sessions/Tabs_13429770734729173 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Shared Dictionary/cache/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Shared Dictionary/cache/index-dir/the-real-index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Shared Dictionary/db +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Shared Dictionary/db-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/SharedStorage +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Shortcuts +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Shortcuts-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Site Characteristics Database/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Site Characteristics Database/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Site Characteristics Database/LOG +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Site Characteristics Database/LOG.old +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Site Characteristics Database/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Sync Data/LevelDB/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Sync Data/LevelDB/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Sync Data/LevelDB/LOG +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Sync Data/LevelDB/LOG.old +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Sync Data/LevelDB/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Top Sites +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Top Sites-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Translate Ranker Model +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/TransportSecurity +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Trust Tokens +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Trust Tokens-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Web Data +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/Web Data-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/WebStorage/QuotaManager +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/WebStorage/QuotaManager-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/chrome_cart_db/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/chrome_cart_db/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/chrome_cart_db/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/commerce_subscription_db/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/commerce_subscription_db/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/commerce_subscription_db/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/discount_infos_db/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/discount_infos_db/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/discount_infos_db/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/discounts_db/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/discounts_db/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/discounts_db/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/engine_allowlist.bf +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/heavy_ad_intervention_opt_out.db +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/heavy_ad_intervention_opt_out.db-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/parcel_tracking_db/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/parcel_tracking_db/LOG +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/parcel_tracking_db/LOG.old +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/LOG +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/LOG.old +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/metadata/CURRENT +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/metadata/LOCK +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/metadata/LOG +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/metadata/LOG.old +3 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/shared_proto_db/metadata/MANIFEST-000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Default/trusted_vault.pb +2 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/DevToolsActivePort +2 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/FileTypePolicies/145.0.7584.0/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/FileTypePolicies/145.0.7584.0/download_file_types.pb +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/FileTypePolicies/145.0.7584.0/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/FirstPartySetsPreloaded/2025.7.24.0/LICENSE +27 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/FirstPartySetsPreloaded/2025.7.24.0/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/FirstPartySetsPreloaded/2025.7.24.0/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/FirstPartySetsPreloaded/2025.7.24.0/sets.json +70 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GPUPersistentCache/GPUCache/NDJN3PYMYS2VQL44TKFDS34NLSXM6YVU/cache.db +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GPUPersistentCache/GPUCache/NDJN3PYMYS2VQL44TKFDS34NLSXM6YVU/cache.db-wal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GPUPersistentCache/GPUCache/NDJN3PYMYS2VQL44TKFDS34NLSXM6YVU/cache.journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GraphiteDawnCache/data_0 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GraphiteDawnCache/data_1 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GraphiteDawnCache/data_2 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GraphiteDawnCache/data_3 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GraphiteDawnCache/f_000001 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GraphiteDawnCache/f_000002 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GraphiteDawnCache/f_000003 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GraphiteDawnCache/f_000004 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/GraphiteDawnCache/index +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Last Version +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Local State +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/MEIPreload/1.1.0.3/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/MEIPreload/1.1.0.3/manifest.json +7 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/MEIPreload/1.1.0.3/preloaded_data.pb +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/PKIMetadata/1728/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/PKIMetadata/1728/crs.pb +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/PKIMetadata/1728/ct_config.pb +325 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/PKIMetadata/1728/kp_pinslist.pb +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/PKIMetadata/1728/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/PKIMetadata/1728/mtc_config.pb +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/SSLErrorAssistant/7/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/SSLErrorAssistant/7/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/SSLErrorAssistant/7/ssl_error_assistant.pb +54 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/SafetyTips/3091/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/SafetyTips/3091/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/SafetyTips/3091/safety_tips.pb +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Subresource Filter/Indexed Rules/38/9.70.0/Ruleset Data +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Subresource Filter/Unindexed Rules/9.70.0/Filtering Rules +6104 -2
- package/.pi/.pi-puppeteer/profiles/chrome/default/Subresource Filter/Unindexed Rules/9.70.0/LICENSE.txt +383 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Subresource Filter/Unindexed Rules/9.70.0/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Subresource Filter/Unindexed Rules/9.70.0/manifest.json +6 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/TrustTokenKeyCommitments/2026.3.23.1/LICENSE +27 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/TrustTokenKeyCommitments/2026.3.23.1/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/TrustTokenKeyCommitments/2026.3.23.1/keys.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/TrustTokenKeyCommitments/2026.3.23.1/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/Variations +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/background_compiled.js +40 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/bindings_main.js +6281 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/bindings_main.wasm +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/offscreen.html +2 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/offscreen_compiled.js +131 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/streaming_worklet_processor.js +78 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/voices.json +1779 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/WasmTtsEngine/20260723.1/wasm_tts_manifest_v3.json +43 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ZxcvbnData/3/_metadata/verified_contents.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ZxcvbnData/3/english_wikipedia.txt +30000 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ZxcvbnData/3/female_names.txt +3712 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ZxcvbnData/3/male_names.txt +984 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ZxcvbnData/3/manifest.json +5 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ZxcvbnData/3/passwords.txt +30000 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ZxcvbnData/3/ranked_dicts +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ZxcvbnData/3/surnames.txt +10000 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/ZxcvbnData/3/us_tv_and_film.txt +19160 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/0d784450fcc5b3ad7ab4efa85b2e1fe56bd5086b69807eab756f62c257ea0d8b +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/0d994b6c9c5d4b0316bcc7a79f4939a2390c638169ea6dc783198c17286d8255 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/3eb16d6c28b502ac4cfee8f4a148df05f4d93229fa36a71db8b08d06329ff18a +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/4448b37acb13eb51a842a8ebc85ea826729571c7293f2213d855d3a76cb9a4f8 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/545666a4efd056351597bb386aea1368105ededc976ed5650d8682daab9f37ff +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/56c21927faa028be6ce18c931660eec37e41da4bfbfd47cafa48350f828c0dbd +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/733776833212346a1018b95197c61bed2d107fbb7790a985ef692780e02f31cb +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/7b05c14dba04ed522210b733f004cb0e74d7679a653b19bd029f9bc0e6b19903 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/8f2c0712b6800fa9622a039cb167cc9503d508a271247e445f08a96eded5772f +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/9bec6c2c0185d3305ac8495047a1aa01e725d58f8f18d219742a2988f07cd93a +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/9f157bd09a986baa46110a0acb7e3d7407208e58d02250c637902962e5ed2c5e +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/abd93867c038d4d17c101ace2226d7e21303d984d7097271392bae6be478495b +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/ae216724654f3e52d3c502328caf83f773e66cc5818c38850165864005a35169 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/c8c2fd0acdb7eecaf4f66d5164204c4e8e5ebbabb6398e0ee6ec6d8af71ea672 +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/fd8fd38c229ab33755ff429ccc9919eba21b566551cbfb17d8e11e35e941ee0e +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/component_crx_cache/metadata.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/extensions_crx_cache/metadata.json +1 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/first_party_sets.db +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/first_party_sets.db-journal +0 -0
- package/.pi/.pi-puppeteer/profiles/chrome/default/segmentation_platform/ukm_db +0 -0
- package/.pi-subagents/artifacts/6d0b4431_custom.vision-agent_0_input.md +54 -0
- package/.pi-subagents/artifacts/6d0b4431_custom.vision-agent_0_meta.json +114 -0
- package/.pi-subagents/artifacts/6d0b4431_custom.vision-agent_0_output.md +22 -0
- package/.pi-subagents/artifacts/6d0b4431_custom.vision-agent_0_transcript.jsonl +7 -0
- package/README.md +0 -1
- package/README.zh-CN.md +0 -1
- package/package.json +2 -1
- package/server/pi-reader.ts +1020 -4
- package/src/App.tsx +5 -0
- package/src/components/dashboard/DashboardPage.tsx +275 -117
- package/src/components/layout/Sidebar.tsx +4 -0
- package/src/components/providers/ProvidersModelsPage.tsx +1830 -0
- package/src/components/sessions/MemoryPage.tsx +480 -85
- package/src/components/sessions/SessionsPage.tsx +473 -89
- package/src/components/settings/SettingsPage.tsx +555 -243
- package/src/components/subagents/SubagentsPage.tsx +502 -0
- package/src/data/builtin-providers.ts +8 -12
- package/src/data/mock-config.ts +1 -15
- package/src/data/mock-usage.ts +0 -2
- package/src/lib/translations/en.ts +174 -23
- package/src/lib/translations/ja.ts +174 -23
- package/src/lib/translations/zh-CN.ts +174 -23
- package/src/lib/translations/zh-TW.ts +174 -23
- package/src/main.tsx +9 -0
- package/src/store/config-store.ts +77 -40
- package/src/types/index.ts +66 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vite.config.ts +164 -4
- package/src/components/models/ModelsPage.tsx +0 -570
- package/src/components/providers/ProvidersPage.tsx +0 -466
|
@@ -0,0 +1,1830 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useConfigStore } from "@/store/config-store";
|
|
3
|
+
import { useTranslation } from "@/lib/i18n";
|
|
4
|
+
import { Badge } from "@/components/ui/Badge";
|
|
5
|
+
import { Modal } from "@/components/ui/Modal";
|
|
6
|
+
import { formatTokens, cn } from "@/lib/utils";
|
|
7
|
+
import type { ApiType, CustomProviderConfig, Model, Provider } from "@/types";
|
|
8
|
+
import {
|
|
9
|
+
Plus,
|
|
10
|
+
Trash2,
|
|
11
|
+
Edit3,
|
|
12
|
+
Eye,
|
|
13
|
+
EyeOff,
|
|
14
|
+
Server,
|
|
15
|
+
Shield,
|
|
16
|
+
Box,
|
|
17
|
+
Brain,
|
|
18
|
+
Image as ImageIcon,
|
|
19
|
+
Search,
|
|
20
|
+
Check,
|
|
21
|
+
X,
|
|
22
|
+
Loader2,
|
|
23
|
+
Zap,
|
|
24
|
+
ClipboardPaste,
|
|
25
|
+
Download,
|
|
26
|
+
SquareCheck,
|
|
27
|
+
Copy,
|
|
28
|
+
} from "lucide-react";
|
|
29
|
+
|
|
30
|
+
const API_TYPES: { value: ApiType; label: string }[] = [
|
|
31
|
+
{ value: "openai-completions", label: "Chat Completions (/chat/completions)" },
|
|
32
|
+
{ value: "openai-responses", label: "OpenAI Responses" },
|
|
33
|
+
{ value: "anthropic-messages", label: "Anthropic Messages" },
|
|
34
|
+
{ value: "google-generative-ai", label: "Google Generative AI" },
|
|
35
|
+
{ value: "google-vertex", label: "Google Vertex AI" },
|
|
36
|
+
{ value: "bedrock-converse-stream", label: "AWS Bedrock" },
|
|
37
|
+
{ value: "mistral-conversations", label: "Mistral" },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
function isValidHttpUrl(value: string): boolean {
|
|
41
|
+
try {
|
|
42
|
+
const u = new URL(value);
|
|
43
|
+
return u.protocol === "http:" || u.protocol === "https:";
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Defaults for models without explicit limits: 256K context, 32K output
|
|
50
|
+
// (32K matches the common max-output ceiling of current mainstream models)
|
|
51
|
+
const DEFAULT_CONTEXT_WINDOW = 262144;
|
|
52
|
+
const DEFAULT_MAX_TOKENS = 32768;
|
|
53
|
+
|
|
54
|
+
// Sanitize to a config-safe id: lowercase letters, digits and hyphens only
|
|
55
|
+
function sanitizeProviderId(name: string): string {
|
|
56
|
+
return name
|
|
57
|
+
.trim()
|
|
58
|
+
.toLowerCase()
|
|
59
|
+
.replace(/\s+/g, "-")
|
|
60
|
+
.replace(/[^a-z0-9-]/g, "")
|
|
61
|
+
.replace(/-+/g, "-")
|
|
62
|
+
.replace(/^-|-$/g, "");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Non-ASCII names (e.g. 中文) sanitize to "" — fall back to the endpoint hostname
|
|
66
|
+
// so the provider still gets a valid id while keeping the original display name.
|
|
67
|
+
function deriveProviderId(name: string, baseUrl: string): string {
|
|
68
|
+
const fromName = sanitizeProviderId(name);
|
|
69
|
+
if (fromName || !name.trim()) return fromName;
|
|
70
|
+
try {
|
|
71
|
+
const host = new URL(baseUrl.trim()).hostname;
|
|
72
|
+
const skip = new Set(["api", "www", "app", "gateway", "open", "openapi", "platform"]);
|
|
73
|
+
const part = host.split(".").find((p) => p && !skip.has(p.toLowerCase()));
|
|
74
|
+
return sanitizeProviderId(part ?? "");
|
|
75
|
+
} catch {
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── Freeform Import Parser ───────────────────────────────
|
|
81
|
+
// Recognizes pasted text like:
|
|
82
|
+
// tokenrouter baseurl:https://api.example.com/v1 key:sk-xxxx
|
|
83
|
+
// modelid:vendor/model-a, vendor/model-b
|
|
84
|
+
// Labels accept half/full-width colons; unlabeled tokens fall back to
|
|
85
|
+
// heuristics (URL → baseUrl, sk-… → apiKey, foo/bar → model id).
|
|
86
|
+
|
|
87
|
+
interface ParsedImport {
|
|
88
|
+
name: string;
|
|
89
|
+
baseUrl: string;
|
|
90
|
+
apiKey: string;
|
|
91
|
+
modelIds: string[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const IMPORT_LABEL_RE =
|
|
95
|
+
/(?<![\w/.\-])(apikey|api_key|api-key|key|token|secret|密钥|金鑰|baseurl|base_url|base-url|url|endpoint|地址|接口|provider|name|名称|名稱|供应商|供應商|model_ids?|modelids?|models?|模型)\s*[::](?!\/\/)/gi;
|
|
96
|
+
|
|
97
|
+
function importField(label: string): "name" | "baseUrl" | "apiKey" | "models" {
|
|
98
|
+
const l = label.toLowerCase();
|
|
99
|
+
if (/^(apikey|api_key|api-key|key|token|secret|密钥|金鑰)$/.test(l)) return "apiKey";
|
|
100
|
+
if (/^(baseurl|base_url|base-url|url|endpoint|地址|接口)$/.test(l)) return "baseUrl";
|
|
101
|
+
if (/^(provider|name|名称|名稱|供应商|供應商)$/.test(l)) return "name";
|
|
102
|
+
return "models";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function parseProviderImport(raw: string): ParsedImport {
|
|
106
|
+
const out: ParsedImport = { name: "", baseUrl: "", apiKey: "", modelIds: [] };
|
|
107
|
+
const pushModels = (value: string) => {
|
|
108
|
+
for (const part of value.split(/[\s,,;;]+/)) {
|
|
109
|
+
const v = part.trim();
|
|
110
|
+
if (v && !out.modelIds.includes(v)) out.modelIds.push(v);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const assignFree = (text: string) => {
|
|
114
|
+
for (const token of text.split(/\s+/)) {
|
|
115
|
+
const v = token.replace(/[,,;;]+$/, "");
|
|
116
|
+
if (!v) continue;
|
|
117
|
+
if (/^https?:\/\//i.test(v)) {
|
|
118
|
+
if (!out.baseUrl) out.baseUrl = v;
|
|
119
|
+
} else if (/^sk-\S{8,}$/i.test(v) || /^[A-Za-z0-9_-]{32,}$/.test(v)) {
|
|
120
|
+
if (!out.apiKey) out.apiKey = v;
|
|
121
|
+
} else if (v.includes("/")) {
|
|
122
|
+
pushModels(v);
|
|
123
|
+
} else if (!out.name) {
|
|
124
|
+
// A label line like "百灵:" keeps a trailing colon — strip it
|
|
125
|
+
out.name = v.replace(/[::]\s*$/, "").trim();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
130
|
+
if (!line.trim()) continue;
|
|
131
|
+
const matches = [...line.matchAll(IMPORT_LABEL_RE)];
|
|
132
|
+
const head = (matches.length ? line.slice(0, matches[0]!.index) : line).trim();
|
|
133
|
+
if (head) assignFree(head);
|
|
134
|
+
matches.forEach((m, i) => {
|
|
135
|
+
const start = m.index! + m[0].length;
|
|
136
|
+
const end = i + 1 < matches.length ? matches[i + 1]!.index! : line.length;
|
|
137
|
+
const value = line.slice(start, end).trim().replace(/[,,;;]+$/, "");
|
|
138
|
+
if (!value) return;
|
|
139
|
+
const field = importField(m[1] ?? "");
|
|
140
|
+
if (field === "models") pushModels(value);
|
|
141
|
+
else if (!out[field]) out[field] = value;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function ProvidersModelsPage() {
|
|
148
|
+
const { t } = useTranslation();
|
|
149
|
+
const { allProviders, auth, modelsJson, removeCustomProvider } = useConfigStore();
|
|
150
|
+
|
|
151
|
+
const builtinProviders = allProviders.filter((p) => p.type === "builtin");
|
|
152
|
+
const customProviders = allProviders.filter((p) => p.type === "custom");
|
|
153
|
+
|
|
154
|
+
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
155
|
+
const [adding, setAdding] = useState(false);
|
|
156
|
+
const [importing, setImporting] = useState(false);
|
|
157
|
+
const [importBump, setImportBump] = useState(0);
|
|
158
|
+
const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);
|
|
159
|
+
const [deleteError, setDeleteError] = useState(false);
|
|
160
|
+
|
|
161
|
+
// Keep a valid selection (default: first custom, else first builtin)
|
|
162
|
+
const selected = allProviders.find((p) => p.id === selectedId) ?? null;
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (!selected && !adding && allProviders.length > 0) {
|
|
165
|
+
setSelectedId(customProviders[0]?.id ?? allProviders[0]?.id ?? null);
|
|
166
|
+
}
|
|
167
|
+
}, [selected, adding, allProviders, customProviders]);
|
|
168
|
+
|
|
169
|
+
const hasKey = (p: Provider) => p.hasAuth || !!p.apiKey || !!auth?.[p.id]?.key;
|
|
170
|
+
|
|
171
|
+
const handleAddProvider = async (id: string, cfg: CustomProviderConfig): Promise<boolean> => {
|
|
172
|
+
const ok = await useConfigStore.getState().addCustomProvider(id, cfg);
|
|
173
|
+
if (ok) {
|
|
174
|
+
setAdding(false);
|
|
175
|
+
setSelectedId(id);
|
|
176
|
+
}
|
|
177
|
+
return ok;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const handleDeleteProvider = async () => {
|
|
181
|
+
if (!deleteConfirm) return;
|
|
182
|
+
setDeleteError(false);
|
|
183
|
+
const ok = await removeCustomProvider(deleteConfirm);
|
|
184
|
+
if (ok) {
|
|
185
|
+
if (selectedId === deleteConfirm) setSelectedId(null);
|
|
186
|
+
setDeleteConfirm(null);
|
|
187
|
+
} else {
|
|
188
|
+
setDeleteError(true);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const handleDuplicateProvider = async (id: string) => {
|
|
193
|
+
const existing = modelsJson?.providers[id];
|
|
194
|
+
if (!existing) return;
|
|
195
|
+
const suffix = "-copy";
|
|
196
|
+
const newId = sanitizeProviderId(id + suffix);
|
|
197
|
+
// Copy config but clear apiKey, authHeader, and models
|
|
198
|
+
const cfg: CustomProviderConfig = {
|
|
199
|
+
baseUrl: existing.baseUrl,
|
|
200
|
+
api: existing.api,
|
|
201
|
+
headers: existing.headers,
|
|
202
|
+
modelOverrides: existing.modelOverrides,
|
|
203
|
+
};
|
|
204
|
+
const ok = await useConfigStore.getState().addCustomProvider(newId, cfg);
|
|
205
|
+
if (ok) setSelectedId(newId);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<div className="space-y-6">
|
|
210
|
+
<div>
|
|
211
|
+
<h1 className="text-2xl font-bold" style={{ color: "var(--page-text)" }}>
|
|
212
|
+
{t("nav.providers_models")}
|
|
213
|
+
</h1>
|
|
214
|
+
<p className="mt-1 text-sm" style={{ color: "var(--muted-text)" }}>
|
|
215
|
+
{t("providers_models.subtitle")}
|
|
216
|
+
</p>
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
<div className="flex overflow-hidden rounded-xl border border-gray-800 bg-gray-900/50">
|
|
220
|
+
{/* ─── Left: Provider List ─────────────────────── */}
|
|
221
|
+
<div className="w-60 shrink-0 border-r border-gray-800 p-3">
|
|
222
|
+
{builtinProviders.length > 0 && (
|
|
223
|
+
<>
|
|
224
|
+
<p className="px-2 pb-2 pt-1 text-xs font-medium uppercase tracking-wider text-gray-500">
|
|
225
|
+
{t("providers.builtin")}
|
|
226
|
+
</p>
|
|
227
|
+
<div className="space-y-0.5">
|
|
228
|
+
{builtinProviders.map((p) => (
|
|
229
|
+
<ProviderListItem
|
|
230
|
+
key={p.id}
|
|
231
|
+
provider={p}
|
|
232
|
+
active={!adding && selectedId === p.id}
|
|
233
|
+
hasKey={hasKey(p)}
|
|
234
|
+
onClick={() => {
|
|
235
|
+
setAdding(false);
|
|
236
|
+
setSelectedId(p.id);
|
|
237
|
+
}}
|
|
238
|
+
/>
|
|
239
|
+
))}
|
|
240
|
+
</div>
|
|
241
|
+
</>
|
|
242
|
+
)}
|
|
243
|
+
|
|
244
|
+
<p className="px-2 pb-2 pt-4 text-xs font-medium uppercase tracking-wider text-gray-500">
|
|
245
|
+
{t("providers_models.custom_providers")}
|
|
246
|
+
</p>
|
|
247
|
+
<div className="space-y-0.5">
|
|
248
|
+
{customProviders.map((p) => (
|
|
249
|
+
<ProviderListItem
|
|
250
|
+
key={p.id}
|
|
251
|
+
provider={p}
|
|
252
|
+
active={!adding && selectedId === p.id}
|
|
253
|
+
hasKey={hasKey(p)}
|
|
254
|
+
onClick={() => {
|
|
255
|
+
setAdding(false);
|
|
256
|
+
setSelectedId(p.id);
|
|
257
|
+
}}
|
|
258
|
+
/>
|
|
259
|
+
))}
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<button
|
|
263
|
+
onClick={() => setAdding(true)}
|
|
264
|
+
className={cn(
|
|
265
|
+
"mt-2 flex w-full items-center gap-2 rounded-lg border px-3 py-2 text-sm font-medium transition-colors",
|
|
266
|
+
adding
|
|
267
|
+
? "border-gray-600 bg-gray-800 text-white"
|
|
268
|
+
: "border-gray-700 text-gray-300 hover:bg-gray-800 hover:text-white"
|
|
269
|
+
)}
|
|
270
|
+
>
|
|
271
|
+
<Plus className="h-4 w-4" />
|
|
272
|
+
{t("providers.add_provider")}
|
|
273
|
+
</button>
|
|
274
|
+
|
|
275
|
+
<button
|
|
276
|
+
onClick={() => setImporting(true)}
|
|
277
|
+
className="mt-2 flex w-full items-center gap-2 rounded-lg border border-gray-700 px-3 py-2 text-sm font-medium text-gray-300 transition-colors hover:bg-gray-800 hover:text-white"
|
|
278
|
+
>
|
|
279
|
+
<ClipboardPaste className="h-4 w-4" />
|
|
280
|
+
{t("providers_models.import")}
|
|
281
|
+
</button>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
{/* ─── Right: Provider Detail / Add Form ───────── */}
|
|
285
|
+
<div className="min-w-0 flex-1 p-6">
|
|
286
|
+
{adding ? (
|
|
287
|
+
<AddProviderForm onSubmit={handleAddProvider} onCancel={() => setAdding(false)} />
|
|
288
|
+
) : selected ? (
|
|
289
|
+
<ProviderDetail
|
|
290
|
+
key={`${selected.id}:${importBump}`}
|
|
291
|
+
provider={selected}
|
|
292
|
+
onDelete={() => setDeleteConfirm(selected.id)}
|
|
293
|
+
onDuplicate={() => handleDuplicateProvider(selected.id)}
|
|
294
|
+
/>
|
|
295
|
+
) : (
|
|
296
|
+
<div className="flex h-40 items-center justify-center text-sm text-gray-500">
|
|
297
|
+
{t("providers_models.select_hint")}
|
|
298
|
+
</div>
|
|
299
|
+
)}
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
{/* Import Provider Modal */}
|
|
304
|
+
<ImportProviderModal
|
|
305
|
+
open={importing}
|
|
306
|
+
onClose={() => setImporting(false)}
|
|
307
|
+
onImported={(id) => {
|
|
308
|
+
setImporting(false);
|
|
309
|
+
setAdding(false);
|
|
310
|
+
setSelectedId(id);
|
|
311
|
+
setImportBump((n) => n + 1);
|
|
312
|
+
}}
|
|
313
|
+
/>
|
|
314
|
+
|
|
315
|
+
{/* Delete Provider Confirmation Modal */}
|
|
316
|
+
<Modal
|
|
317
|
+
open={!!deleteConfirm}
|
|
318
|
+
onClose={() => { setDeleteConfirm(null); setDeleteError(false); }}
|
|
319
|
+
title={t("providers.delete_provider")}
|
|
320
|
+
>
|
|
321
|
+
<div className="space-y-4">
|
|
322
|
+
<div className="flex items-start gap-3">
|
|
323
|
+
<Trash2 className="h-5 w-5 shrink-0 mt-0.5 text-red-400" />
|
|
324
|
+
<div>
|
|
325
|
+
<p className="text-sm text-gray-200">
|
|
326
|
+
<strong>{allProviders.find((p) => p.id === deleteConfirm)?.name}</strong>
|
|
327
|
+
{" — "}
|
|
328
|
+
{t("providers.delete_confirm")}
|
|
329
|
+
</p>
|
|
330
|
+
<p className="text-xs mt-2 text-gray-500">
|
|
331
|
+
{t("providers_models.delete_provider_note")}
|
|
332
|
+
</p>
|
|
333
|
+
{deleteError && (
|
|
334
|
+
<p className="text-xs mt-2 text-red-400">{t("providers_models.save_failed")}</p>
|
|
335
|
+
)}
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
<div className="flex justify-end gap-3">
|
|
339
|
+
<button
|
|
340
|
+
onClick={() => { setDeleteConfirm(null); setDeleteError(false); }}
|
|
341
|
+
className="rounded-lg px-4 py-2 text-sm text-gray-400 hover:bg-gray-800"
|
|
342
|
+
>
|
|
343
|
+
{t("models.cancel")}
|
|
344
|
+
</button>
|
|
345
|
+
<button
|
|
346
|
+
onClick={handleDeleteProvider}
|
|
347
|
+
className="flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white"
|
|
348
|
+
style={{ backgroundColor: "#dc2626" }}
|
|
349
|
+
>
|
|
350
|
+
<Trash2 className="h-4 w-4" />
|
|
351
|
+
{t("providers.delete_provider")}
|
|
352
|
+
</button>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
</Modal>
|
|
356
|
+
</div>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ─── Provider List Item ───────────────────────────────────
|
|
361
|
+
|
|
362
|
+
function ProviderListItem({
|
|
363
|
+
provider,
|
|
364
|
+
active,
|
|
365
|
+
hasKey,
|
|
366
|
+
onClick,
|
|
367
|
+
}: {
|
|
368
|
+
provider: Provider;
|
|
369
|
+
active: boolean;
|
|
370
|
+
hasKey: boolean;
|
|
371
|
+
onClick: () => void;
|
|
372
|
+
}) {
|
|
373
|
+
return (
|
|
374
|
+
<button
|
|
375
|
+
onClick={onClick}
|
|
376
|
+
className={cn(
|
|
377
|
+
"flex w-full items-center gap-2 rounded-lg border px-3 py-2 text-left text-sm transition-colors",
|
|
378
|
+
active
|
|
379
|
+
? "border-gray-600 bg-gray-800 text-white"
|
|
380
|
+
: "border-transparent text-gray-300 hover:bg-gray-800/60"
|
|
381
|
+
)}
|
|
382
|
+
>
|
|
383
|
+
{provider.type === "custom" ? (
|
|
384
|
+
<Server className="h-4 w-4 shrink-0 text-blue-400" />
|
|
385
|
+
) : (
|
|
386
|
+
<Shield className="h-4 w-4 shrink-0 text-emerald-400" />
|
|
387
|
+
)}
|
|
388
|
+
<span className="min-w-0 flex-1 truncate">{provider.name}</span>
|
|
389
|
+
<span
|
|
390
|
+
className={cn("h-2 w-2 shrink-0 rounded-full", hasKey ? "bg-emerald-400" : "bg-gray-600")}
|
|
391
|
+
/>
|
|
392
|
+
</button>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// ─── Connection Test Button ───────────────────────────────
|
|
397
|
+
|
|
398
|
+
type TestState =
|
|
399
|
+
| { status: "idle" }
|
|
400
|
+
| { status: "testing" }
|
|
401
|
+
| { status: "ok"; latencyMs: number }
|
|
402
|
+
| { status: "fail"; message: string };
|
|
403
|
+
|
|
404
|
+
function TestConnectionButton({ baseUrl, apiKey }: { baseUrl: string; apiKey?: string }) {
|
|
405
|
+
const { t } = useTranslation();
|
|
406
|
+
const [test, setTest] = useState<TestState>({ status: "idle" });
|
|
407
|
+
|
|
408
|
+
const runTest = async () => {
|
|
409
|
+
setTest({ status: "testing" });
|
|
410
|
+
try {
|
|
411
|
+
const res = await fetch("/api/pi/provider-test", {
|
|
412
|
+
method: "POST",
|
|
413
|
+
headers: { "Content-Type": "application/json" },
|
|
414
|
+
body: JSON.stringify({ baseUrl: baseUrl.trim(), apiKey: apiKey || undefined }),
|
|
415
|
+
});
|
|
416
|
+
const data = await res.json();
|
|
417
|
+
if (data.success) {
|
|
418
|
+
setTest({ status: "ok", latencyMs: data.latencyMs ?? 0 });
|
|
419
|
+
} else {
|
|
420
|
+
setTest({ status: "fail", message: data.message ?? "unknown" });
|
|
421
|
+
}
|
|
422
|
+
} catch {
|
|
423
|
+
setTest({ status: "fail", message: "network error" });
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const disabled = !baseUrl.trim() || !isValidHttpUrl(baseUrl.trim()) || test.status === "testing";
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
<div className="flex items-center gap-3">
|
|
431
|
+
<button
|
|
432
|
+
onClick={runTest}
|
|
433
|
+
disabled={disabled}
|
|
434
|
+
className="flex items-center gap-2 rounded-lg border border-gray-700 px-3 py-1.5 text-sm text-gray-300 transition-colors hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-50"
|
|
435
|
+
>
|
|
436
|
+
{test.status === "testing" ? (
|
|
437
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
438
|
+
) : (
|
|
439
|
+
<Zap className="h-4 w-4" />
|
|
440
|
+
)}
|
|
441
|
+
{test.status === "testing" ? t("providers_models.testing") : t("providers_models.test_connection")}
|
|
442
|
+
</button>
|
|
443
|
+
{test.status === "ok" && (
|
|
444
|
+
<span className="flex items-center gap-1 text-sm text-emerald-400">
|
|
445
|
+
<Check className="h-4 w-4" />
|
|
446
|
+
{t("providers_models.test_ok", String(test.latencyMs))}
|
|
447
|
+
</span>
|
|
448
|
+
)}
|
|
449
|
+
{test.status === "fail" && (
|
|
450
|
+
<span className="flex items-center gap-1 text-sm text-red-400">
|
|
451
|
+
<X className="h-4 w-4" />
|
|
452
|
+
{t("providers_models.test_fail", test.message)}
|
|
453
|
+
</span>
|
|
454
|
+
)}
|
|
455
|
+
</div>
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// ─── Provider Detail Panel ────────────────────────────────
|
|
460
|
+
|
|
461
|
+
function ProviderDetail({ provider, onDelete, onDuplicate }: { provider: Provider; onDelete: () => void; onDuplicate: () => void }) {
|
|
462
|
+
const { t } = useTranslation();
|
|
463
|
+
const {
|
|
464
|
+
auth,
|
|
465
|
+
settings,
|
|
466
|
+
updateSettings,
|
|
467
|
+
updateCustomProvider,
|
|
468
|
+
setProviderAuth,
|
|
469
|
+
removeProviderAuth,
|
|
470
|
+
addModel,
|
|
471
|
+
updateModel,
|
|
472
|
+
removeModel,
|
|
473
|
+
addEnabledModel,
|
|
474
|
+
removeEnabledModel,
|
|
475
|
+
} = useConfigStore();
|
|
476
|
+
|
|
477
|
+
const isCustom = provider.type === "custom";
|
|
478
|
+
const savedKey = provider.apiKey ?? auth?.[provider.id]?.key ?? "";
|
|
479
|
+
|
|
480
|
+
const [providerName, setProviderName] = useState(provider.name ?? "");
|
|
481
|
+
const [baseUrl, setBaseUrl] = useState(provider.baseUrl ?? "");
|
|
482
|
+
const [api, setApi] = useState<ApiType>(provider.api ?? "openai-completions");
|
|
483
|
+
const [apiKey, setApiKey] = useState(savedKey);
|
|
484
|
+
const [showKey, setShowKey] = useState(false);
|
|
485
|
+
const [editModel, setEditModel] = useState<Model | null>(null);
|
|
486
|
+
const [showAddModel, setShowAddModel] = useState(false);
|
|
487
|
+
const [deleteModel, setDeleteModel] = useState<Model | null>(null);
|
|
488
|
+
const [modelQuery, setModelQuery] = useState("");
|
|
489
|
+
const [saveState, setSaveState] = useState<"idle" | "saving" | "saved" | "error">("idle");
|
|
490
|
+
|
|
491
|
+
// ─── Fetch Models State ───
|
|
492
|
+
const [fetchOpen, setFetchOpen] = useState(false);
|
|
493
|
+
const [fetching, setFetching] = useState(false);
|
|
494
|
+
const [fetchError, setFetchError] = useState<string | null>(null);
|
|
495
|
+
const [fetchedModels, setFetchedModels] = useState<{ id: string; contextWindow?: number; maxTokens?: number; vision?: boolean }[]>([]);
|
|
496
|
+
const [fetchSelected, setFetchSelected] = useState<Set<string>>(new Set());
|
|
497
|
+
const [fetchImported, setFetchImported] = useState<number | null>(null);
|
|
498
|
+
|
|
499
|
+
// ─── Per-Model Test State ───
|
|
500
|
+
const [modelTests, setModelTests] = useState<Map<string, TestState>>(new Map());
|
|
501
|
+
const getModelTest = (modelId: string) => modelTests.get(modelId) ?? { status: "idle" };
|
|
502
|
+
|
|
503
|
+
const existingModelIds = new Set(provider.models.map((m) => m.id));
|
|
504
|
+
const availableModels = fetchedModels.filter((m) => !existingModelIds.has(m.id));
|
|
505
|
+
const isSelected = (id: string) => fetchSelected.has(id);
|
|
506
|
+
const allSelected = availableModels.length > 0 && availableModels.every((m) => isSelected(m.id));
|
|
507
|
+
|
|
508
|
+
const toggleSelect = (id: string) => {
|
|
509
|
+
setFetchSelected((prev) => {
|
|
510
|
+
const next = new Set(prev);
|
|
511
|
+
next.has(id) ? next.delete(id) : next.add(id);
|
|
512
|
+
return next;
|
|
513
|
+
});
|
|
514
|
+
};
|
|
515
|
+
const toggleAll = () => {
|
|
516
|
+
setFetchSelected((prev) => {
|
|
517
|
+
if (availableModels.every((m) => isSelected(m.id))) return new Set();
|
|
518
|
+
return new Set(availableModels.map((m) => m.id));
|
|
519
|
+
});
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
const fetchModels = async () => {
|
|
523
|
+
if (!baseUrl.trim() || !isValidHttpUrl(baseUrl.trim())) return;
|
|
524
|
+
setFetchOpen(true);
|
|
525
|
+
setFetching(true);
|
|
526
|
+
setFetchError(null);
|
|
527
|
+
setFetchedModels([]);
|
|
528
|
+
setFetchSelected(new Set());
|
|
529
|
+
setFetchImported(null);
|
|
530
|
+
try {
|
|
531
|
+
const res = await fetch("/api/pi/provider-models", {
|
|
532
|
+
method: "POST",
|
|
533
|
+
headers: { "Content-Type": "application/json" },
|
|
534
|
+
body: JSON.stringify({ baseUrl: baseUrl.trim(), apiKey, providerId: provider.id }),
|
|
535
|
+
});
|
|
536
|
+
const data = await res.json();
|
|
537
|
+
if (data.error) setFetchError(data.error);
|
|
538
|
+
else setFetchedModels(data.models ?? []);
|
|
539
|
+
} catch {
|
|
540
|
+
setFetchError("network error");
|
|
541
|
+
} finally {
|
|
542
|
+
setFetching(false);
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
const handleImportFetched = async () => {
|
|
547
|
+
const selected = availableModels.filter((m) => isSelected(m.id));
|
|
548
|
+
if (selected.length === 0) return;
|
|
549
|
+
selected.forEach((m) => {
|
|
550
|
+
addModel(provider.id, { id: m.id, contextWindow: m.contextWindow ?? DEFAULT_CONTEXT_WINDOW, maxTokens: m.maxTokens ?? DEFAULT_MAX_TOKENS, input: m.vision ? ["text", "image"] : ["text"] } as Model);
|
|
551
|
+
});
|
|
552
|
+
// Enable in a single batched write — per-model addEnabledModel calls race
|
|
553
|
+
// on the same settings snapshot and overwrite each other.
|
|
554
|
+
const list = settings?.enabledModels ?? [];
|
|
555
|
+
const refs = selected.map((m) => `${provider.id}/${m.id}`);
|
|
556
|
+
await updateSettings({ enabledModels: Array.from(new Set([...list, ...refs])) });
|
|
557
|
+
setFetchImported(selected.length);
|
|
558
|
+
setTimeout(() => {
|
|
559
|
+
setFetchOpen(false); setFetchedModels([]); setFetchSelected(new Set());
|
|
560
|
+
setFetchImported(null); setFetchError(null);
|
|
561
|
+
}, 1500);
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
const handleTestModel = async (m: Model) => {
|
|
565
|
+
const modelId = m.id;
|
|
566
|
+
setModelTests((prev) => {
|
|
567
|
+
const next = new Map(prev);
|
|
568
|
+
next.set(modelId, { status: "testing" });
|
|
569
|
+
return next;
|
|
570
|
+
});
|
|
571
|
+
try {
|
|
572
|
+
const res = await fetch("/api/pi/model-test", {
|
|
573
|
+
method: "POST",
|
|
574
|
+
headers: { "Content-Type": "application/json" },
|
|
575
|
+
body: JSON.stringify({ baseUrl: baseUrl.trim(), modelId, apiKey, apiType: api ?? undefined }),
|
|
576
|
+
});
|
|
577
|
+
const data = await res.json();
|
|
578
|
+
setModelTests((prev) => {
|
|
579
|
+
const next = new Map(prev);
|
|
580
|
+
next.set(modelId, data.success
|
|
581
|
+
? { status: "ok" as const, latencyMs: data.latencyMs ?? 0 }
|
|
582
|
+
: { status: "fail" as const, message: data.message ?? "unknown" });
|
|
583
|
+
return next;
|
|
584
|
+
});
|
|
585
|
+
} catch {
|
|
586
|
+
setModelTests((prev) => {
|
|
587
|
+
const next = new Map(prev);
|
|
588
|
+
next.set(modelId, { status: "fail" as const, message: "network error" });
|
|
589
|
+
return next;
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
const urlInvalid = isCustom && baseUrl.trim() !== "" && !isValidHttpUrl(baseUrl.trim());
|
|
595
|
+
|
|
596
|
+
const dirty =
|
|
597
|
+
(isCustom && (providerName !== (provider.name ?? "") || baseUrl !== (provider.baseUrl ?? "") || api !== (provider.api ?? "openai-completions"))) ||
|
|
598
|
+
apiKey !== savedKey;
|
|
599
|
+
|
|
600
|
+
const handleSave = async () => {
|
|
601
|
+
setSaveState("saving");
|
|
602
|
+
let ok = true;
|
|
603
|
+
if (isCustom) {
|
|
604
|
+
ok = await updateCustomProvider(provider.id, {
|
|
605
|
+
name: providerName || undefined,
|
|
606
|
+
baseUrl: baseUrl || undefined,
|
|
607
|
+
api,
|
|
608
|
+
apiKey: apiKey || undefined,
|
|
609
|
+
});
|
|
610
|
+
} else if (apiKey !== savedKey) {
|
|
611
|
+
ok = apiKey
|
|
612
|
+
? await setProviderAuth(provider.id, apiKey)
|
|
613
|
+
: await removeProviderAuth(provider.id);
|
|
614
|
+
}
|
|
615
|
+
setSaveState(ok ? "saved" : "error");
|
|
616
|
+
if (ok) setTimeout(() => setSaveState("idle"), 2500);
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// Enabled state lives in settings.enabledModels ("provider/model" refs) —
|
|
620
|
+
// the same source pi reads, so toggling here matches the CLI behavior.
|
|
621
|
+
const isEnabled = (modelId: string) =>
|
|
622
|
+
settings?.enabledModels?.includes(`${provider.id}/${modelId}`) ?? false;
|
|
623
|
+
|
|
624
|
+
const handleToggle = (modelId: string) => {
|
|
625
|
+
const ref = `${provider.id}/${modelId}`;
|
|
626
|
+
if (isEnabled(modelId)) removeEnabledModel(ref);
|
|
627
|
+
else addEnabledModel(ref);
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
const setAllEnabled = (enable: boolean) => {
|
|
631
|
+
const list = settings?.enabledModels ?? [];
|
|
632
|
+
const refs = provider.models.map((m) => `${provider.id}/${m.id}`);
|
|
633
|
+
const next = enable
|
|
634
|
+
? Array.from(new Set([...list, ...refs]))
|
|
635
|
+
: list.filter((r) => !refs.includes(r));
|
|
636
|
+
updateSettings({ enabledModels: next });
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
const q = modelQuery.trim().toLowerCase();
|
|
640
|
+
const visibleModels = q
|
|
641
|
+
? provider.models.filter(
|
|
642
|
+
(m) => m.id.toLowerCase().includes(q) || (m.name ?? "").toLowerCase().includes(q)
|
|
643
|
+
)
|
|
644
|
+
: provider.models;
|
|
645
|
+
|
|
646
|
+
return (
|
|
647
|
+
<div className="space-y-5">
|
|
648
|
+
{/* Header */}
|
|
649
|
+
<div className="flex items-center gap-3">
|
|
650
|
+
<h2 className="text-lg font-semibold text-white">{provider.name}</h2>
|
|
651
|
+
<Badge variant={isCustom ? "default" : "info"}>
|
|
652
|
+
{isCustom ? t("providers.custom") : t("providers.builtin")}
|
|
653
|
+
</Badge>
|
|
654
|
+
{savedKey && <Badge variant="success">{t("providers.configured")}</Badge>}
|
|
655
|
+
{isCustom && (
|
|
656
|
+
<button
|
|
657
|
+
onClick={onDuplicate}
|
|
658
|
+
className="rounded-lg p-2 text-gray-500 transition-colors hover:bg-blue-500/10 hover:text-blue-400"
|
|
659
|
+
title={t("providers.duplicate_provider")}
|
|
660
|
+
>
|
|
661
|
+
<Copy className="h-4 w-4" />
|
|
662
|
+
</button>
|
|
663
|
+
)}
|
|
664
|
+
{isCustom && (
|
|
665
|
+
<button
|
|
666
|
+
onClick={onDelete}
|
|
667
|
+
className="ml-auto rounded-lg p-2 text-gray-500 transition-colors hover:bg-red-500/10 hover:text-red-400"
|
|
668
|
+
title={t("providers.delete_provider")}
|
|
669
|
+
>
|
|
670
|
+
<Trash2 className="h-4 w-4" />
|
|
671
|
+
</button>
|
|
672
|
+
)}
|
|
673
|
+
</div>
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
{/* Name (custom only) */}
|
|
677
|
+
{isCustom && (
|
|
678
|
+
<div>
|
|
679
|
+
<label className="block text-sm text-gray-400">{t("providers_models.name")}</label>
|
|
680
|
+
<input
|
|
681
|
+
type="text"
|
|
682
|
+
value={providerName}
|
|
683
|
+
onChange={(e) => setProviderName(e.target.value)}
|
|
684
|
+
placeholder="My Provider"
|
|
685
|
+
className="mt-1.5 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2.5 text-sm text-white"
|
|
686
|
+
/>
|
|
687
|
+
</div>
|
|
688
|
+
)}
|
|
689
|
+
{/* Base URL */}
|
|
690
|
+
<div>
|
|
691
|
+
<label className="block text-sm text-gray-400">{t("providers.base_url")}</label>
|
|
692
|
+
<input
|
|
693
|
+
type="text"
|
|
694
|
+
value={baseUrl}
|
|
695
|
+
disabled={!isCustom}
|
|
696
|
+
onChange={(e) => setBaseUrl(e.target.value)}
|
|
697
|
+
placeholder="https://api.example.com/v1"
|
|
698
|
+
className={cn(
|
|
699
|
+
"mt-1.5 w-full rounded-lg border bg-gray-800 px-3 py-2.5 text-sm text-white disabled:opacity-50",
|
|
700
|
+
urlInvalid ? "border-red-500" : "border-gray-700"
|
|
701
|
+
)}
|
|
702
|
+
/>
|
|
703
|
+
{urlInvalid && (
|
|
704
|
+
<p className="mt-1 text-xs text-red-400">{t("providers_models.invalid_url")}</p>
|
|
705
|
+
)}
|
|
706
|
+
</div>
|
|
707
|
+
|
|
708
|
+
{/* API Type */}
|
|
709
|
+
<div>
|
|
710
|
+
<label className="block text-sm text-gray-400">{t("providers.api_type")}</label>
|
|
711
|
+
<select
|
|
712
|
+
value={api}
|
|
713
|
+
disabled={!isCustom}
|
|
714
|
+
onChange={(e) => setApi(e.target.value as ApiType)}
|
|
715
|
+
className="mt-1.5 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2.5 text-sm text-white disabled:opacity-50"
|
|
716
|
+
>
|
|
717
|
+
{API_TYPES.map((a) => (
|
|
718
|
+
<option key={a.value} value={a.value}>{a.label}</option>
|
|
719
|
+
))}
|
|
720
|
+
</select>
|
|
721
|
+
</div>
|
|
722
|
+
|
|
723
|
+
{/* API Key */}
|
|
724
|
+
<div>
|
|
725
|
+
<label className="block text-sm text-gray-400">{t("providers.api_key")}</label>
|
|
726
|
+
<div className="relative mt-1.5">
|
|
727
|
+
<input
|
|
728
|
+
type={showKey ? "text" : "password"}
|
|
729
|
+
value={apiKey}
|
|
730
|
+
onChange={(e) => setApiKey(e.target.value)}
|
|
731
|
+
placeholder="sk-... or $MY_API_KEY"
|
|
732
|
+
className="w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2.5 pr-10 text-sm text-white"
|
|
733
|
+
/>
|
|
734
|
+
<button
|
|
735
|
+
onClick={() => setShowKey(!showKey)}
|
|
736
|
+
className="absolute right-2 top-1/2 -translate-y-1/2 rounded-md p-1.5 text-gray-500 hover:text-gray-300"
|
|
737
|
+
>
|
|
738
|
+
{showKey ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
|
739
|
+
</button>
|
|
740
|
+
</div>
|
|
741
|
+
</div>
|
|
742
|
+
|
|
743
|
+
{/* Save / Test / Feedback row */}
|
|
744
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
745
|
+
{dirty && (
|
|
746
|
+
<button
|
|
747
|
+
onClick={handleSave}
|
|
748
|
+
disabled={saveState === "saving" || urlInvalid}
|
|
749
|
+
className="flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50"
|
|
750
|
+
style={{ backgroundColor: "#3b82f6", color: "#ffffff" }}
|
|
751
|
+
>
|
|
752
|
+
{saveState === "saving" && <Loader2 className="h-4 w-4 animate-spin" />}
|
|
753
|
+
{t("models.save")}
|
|
754
|
+
</button>
|
|
755
|
+
)}
|
|
756
|
+
{saveState === "saved" && (
|
|
757
|
+
<span className="flex items-center gap-1 text-sm text-emerald-400">
|
|
758
|
+
<Check className="h-4 w-4" />
|
|
759
|
+
{t("providers_models.saved")}
|
|
760
|
+
</span>
|
|
761
|
+
)}
|
|
762
|
+
{saveState === "error" && (
|
|
763
|
+
<span className="flex items-center gap-1 text-sm text-red-400">
|
|
764
|
+
<X className="h-4 w-4" />
|
|
765
|
+
{t("providers_models.save_failed")}
|
|
766
|
+
</span>
|
|
767
|
+
)}
|
|
768
|
+
{isCustom && baseUrl.trim() !== "" && (
|
|
769
|
+
<TestConnectionButton baseUrl={baseUrl} apiKey={apiKey} />
|
|
770
|
+
)}
|
|
771
|
+
</div>
|
|
772
|
+
|
|
773
|
+
{/* Model List */}
|
|
774
|
+
<div>
|
|
775
|
+
<div className="flex items-center justify-between gap-3">
|
|
776
|
+
<label className="block text-sm text-gray-400">{t("providers_models.model_list")}</label>
|
|
777
|
+
{provider.models.length > 0 && (
|
|
778
|
+
<div className="flex items-center gap-2">
|
|
779
|
+
<button
|
|
780
|
+
onClick={() => setAllEnabled(true)}
|
|
781
|
+
className="rounded-md px-2 py-1 text-xs text-emerald-400 transition-colors hover:bg-emerald-500/10"
|
|
782
|
+
>
|
|
783
|
+
{t("providers_models.enable_all")}
|
|
784
|
+
</button>
|
|
785
|
+
<button
|
|
786
|
+
onClick={() => setAllEnabled(false)}
|
|
787
|
+
className="rounded-md px-2 py-1 text-xs text-gray-400 transition-colors hover:bg-gray-800"
|
|
788
|
+
>
|
|
789
|
+
{t("providers_models.disable_all")}
|
|
790
|
+
</button>
|
|
791
|
+
</div>
|
|
792
|
+
)}
|
|
793
|
+
</div>
|
|
794
|
+
|
|
795
|
+
{provider.models.length > 5 && (
|
|
796
|
+
<div className="relative mt-1.5">
|
|
797
|
+
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-500" />
|
|
798
|
+
<input
|
|
799
|
+
type="text"
|
|
800
|
+
value={modelQuery}
|
|
801
|
+
onChange={(e) => setModelQuery(e.target.value)}
|
|
802
|
+
placeholder={t("models.search_placeholder")}
|
|
803
|
+
className="w-full rounded-lg border border-gray-700 bg-gray-800 py-2 pl-9 pr-3 text-sm text-white"
|
|
804
|
+
/>
|
|
805
|
+
</div>
|
|
806
|
+
)}
|
|
807
|
+
|
|
808
|
+
<div className="mt-1.5 space-y-2 rounded-lg border border-gray-800 p-3">
|
|
809
|
+
{provider.models.length === 0 && (
|
|
810
|
+
<p className="px-1 py-2 text-sm text-gray-500">{t("models.no_models")}</p>
|
|
811
|
+
)}
|
|
812
|
+
{provider.models.length > 0 && visibleModels.length === 0 && (
|
|
813
|
+
<p className="px-1 py-2 text-sm text-gray-500">{t("models.no_models")}</p>
|
|
814
|
+
)}
|
|
815
|
+
{visibleModels.map((m) => (
|
|
816
|
+
<div
|
|
817
|
+
key={m.id}
|
|
818
|
+
className={cn(
|
|
819
|
+
"flex items-center gap-2 rounded-lg border border-gray-700 bg-gray-900/70 px-3 py-2.5",
|
|
820
|
+
!isEnabled(m.id) && "opacity-50"
|
|
821
|
+
)}
|
|
822
|
+
>
|
|
823
|
+
<Box className="h-4 w-4 shrink-0 text-gray-500" />
|
|
824
|
+
<span className="min-w-0 flex-1 truncate font-mono text-sm text-gray-200">
|
|
825
|
+
{m.id}
|
|
826
|
+
</span>
|
|
827
|
+
{m.reasoning && (
|
|
828
|
+
<span title={t("models.reasoning")} className="flex shrink-0">
|
|
829
|
+
<Brain className="h-3.5 w-3.5 text-purple-400" />
|
|
830
|
+
</span>
|
|
831
|
+
)}
|
|
832
|
+
{m.input?.includes("image") && (
|
|
833
|
+
<span title={t("models.image_input")} className="flex shrink-0">
|
|
834
|
+
<ImageIcon className="h-3.5 w-3.5 text-blue-400" />
|
|
835
|
+
</span>
|
|
836
|
+
)}
|
|
837
|
+
<span className="rounded-md border border-gray-600 bg-gray-800/50 px-2 py-0.5 text-xs text-gray-400">
|
|
838
|
+
{m.cost && (m.cost.input || m.cost.output)
|
|
839
|
+
? `$${m.cost.input}/${m.cost.output}`
|
|
840
|
+
: t("models.free")}
|
|
841
|
+
</span>
|
|
842
|
+
<span className="rounded-md border border-gray-600 bg-gray-800/50 px-2 py-0.5 text-xs text-gray-400">
|
|
843
|
+
{m.contextWindow ? formatTokens(m.contextWindow) : "—"}
|
|
844
|
+
</span>
|
|
845
|
+
<button
|
|
846
|
+
onClick={() => handleToggle(m.id)}
|
|
847
|
+
className={cn(
|
|
848
|
+
"rounded-md px-2 py-1 text-xs transition-colors",
|
|
849
|
+
isEnabled(m.id)
|
|
850
|
+
? "text-emerald-400 hover:bg-emerald-500/10"
|
|
851
|
+
: "text-gray-500 hover:bg-gray-700"
|
|
852
|
+
)}
|
|
853
|
+
>
|
|
854
|
+
{isEnabled(m.id) ? t("models.enabled") : t("models.disabled")}
|
|
855
|
+
</button>
|
|
856
|
+
{/* Test model */}
|
|
857
|
+
{(() => {
|
|
858
|
+
const test = getModelTest(m.id);
|
|
859
|
+
if (test.status === "testing") return null;
|
|
860
|
+
return (
|
|
861
|
+
<button
|
|
862
|
+
onClick={() => handleTestModel(m)}
|
|
863
|
+
className="rounded-md p-1.5 text-gray-500 transition-colors hover:bg-gray-700 hover:text-gray-200"
|
|
864
|
+
title={t("models.test_model")}
|
|
865
|
+
>
|
|
866
|
+
<Zap className="h-3.5 w-3.5" />
|
|
867
|
+
</button>
|
|
868
|
+
);
|
|
869
|
+
})()}
|
|
870
|
+
{/* Model test result */}
|
|
871
|
+
{(() => {
|
|
872
|
+
const test = getModelTest(m.id);
|
|
873
|
+
if (test.status === "ok") return (
|
|
874
|
+
<span className="flex items-center gap-1 text-xs text-emerald-400">
|
|
875
|
+
<Check className="h-3.5 w-3.5" />
|
|
876
|
+
{t("providers_models.test_ok", String(test.latencyMs))}
|
|
877
|
+
</span>
|
|
878
|
+
);
|
|
879
|
+
if (test.status === "fail") return (
|
|
880
|
+
<span className="flex items-center gap-1 text-xs text-red-400">
|
|
881
|
+
<X className="h-3.5 w-3.5" />
|
|
882
|
+
{t("providers_models.test_fail", test.message)}
|
|
883
|
+
</span>
|
|
884
|
+
);
|
|
885
|
+
if (test.status === "testing") return (
|
|
886
|
+
<span className="flex items-center gap-1 text-xs text-gray-400">
|
|
887
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
888
|
+
{t("providers_models.testing")}
|
|
889
|
+
</span>
|
|
890
|
+
);
|
|
891
|
+
return null;
|
|
892
|
+
})()}
|
|
893
|
+
<button
|
|
894
|
+
onClick={() => setEditModel(m)}
|
|
895
|
+
className="rounded-md p-1.5 text-gray-500 transition-colors hover:bg-gray-700 hover:text-gray-200"
|
|
896
|
+
title={t("models.edit_model")}
|
|
897
|
+
>
|
|
898
|
+
<Edit3 className="h-3.5 w-3.5" />
|
|
899
|
+
</button>
|
|
900
|
+
<button
|
|
901
|
+
onClick={() => setDeleteModel(m)}
|
|
902
|
+
className="rounded-md p-1.5 text-gray-500 transition-colors hover:bg-red-500/10 hover:text-red-400"
|
|
903
|
+
title={t("models.delete_model")}
|
|
904
|
+
>
|
|
905
|
+
<Trash2 className="h-3.5 w-3.5" />
|
|
906
|
+
</button>
|
|
907
|
+
</div>
|
|
908
|
+
))}
|
|
909
|
+
</div>
|
|
910
|
+
<div className="flex flex-wrap gap-2 mt-3">
|
|
911
|
+
<button
|
|
912
|
+
onClick={fetchModels}
|
|
913
|
+
disabled={!baseUrl.trim() || !isValidHttpUrl(baseUrl.trim()) || fetching}
|
|
914
|
+
title={
|
|
915
|
+
!baseUrl.trim() || !isValidHttpUrl(baseUrl.trim())
|
|
916
|
+
? t("providers_models.fetch_no_endpoint")
|
|
917
|
+
: t("providers_models.fetch_models")
|
|
918
|
+
}
|
|
919
|
+
className="flex items-center gap-2 rounded-lg border border-gray-700 px-4 py-2 text-sm text-gray-300 transition-colors hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-50"
|
|
920
|
+
>
|
|
921
|
+
{fetching ? <Loader2 className="h-4 w-4 animate-spin" /> : <Download className="h-4 w-4" />}
|
|
922
|
+
{fetching ? t("providers_models.fetching") : t("providers_models.fetch_models")}
|
|
923
|
+
</button>
|
|
924
|
+
<button
|
|
925
|
+
onClick={() => setShowAddModel(true)}
|
|
926
|
+
className="flex items-center gap-2 rounded-lg border border-gray-700 px-4 py-2 text-sm text-gray-300 transition-colors hover:bg-gray-800"
|
|
927
|
+
>
|
|
928
|
+
<Plus className="h-4 w-4" />
|
|
929
|
+
{t("models.add_model")}
|
|
930
|
+
</button>
|
|
931
|
+
</div>
|
|
932
|
+
</div>
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
{/* Fetch Models Modal */}
|
|
936
|
+
<Modal
|
|
937
|
+
open={fetchOpen}
|
|
938
|
+
onClose={() => { setFetchOpen(false); setFetchedModels([]); setFetchSelected(new Set()); setFetchError(null); setFetchImported(null); }}
|
|
939
|
+
title={t("providers_models.fetch_title")}
|
|
940
|
+
size="lg"
|
|
941
|
+
>
|
|
942
|
+
<div className="space-y-4">
|
|
943
|
+
<p className="text-sm text-gray-400">{t("providers_models.fetch_desc", provider.name)}</p>
|
|
944
|
+
{fetching && (
|
|
945
|
+
<div className="flex items-center gap-2 py-6 text-gray-400">
|
|
946
|
+
<Loader2 className="h-5 w-5 animate-spin" />
|
|
947
|
+
<span>{t("providers_models.fetching")}</span>
|
|
948
|
+
</div>
|
|
949
|
+
)}
|
|
950
|
+
{fetchError && !fetching && (
|
|
951
|
+
<div className="flex items-start gap-3 rounded-lg border border-red-500/30 bg-red-500/10 p-4">
|
|
952
|
+
<X className="h-5 w-5 shrink-0 text-red-400 mt-0.5" />
|
|
953
|
+
<p className="text-sm text-red-300">{t("providers_models.fetch_error", fetchError)}</p>
|
|
954
|
+
</div>
|
|
955
|
+
)}
|
|
956
|
+
{!fetching && !fetchError && availableModels.length === 0 && fetchedModels.length === 0 && (
|
|
957
|
+
<p className="text-sm text-gray-500">{t("providers_models.fetch_empty")}</p>
|
|
958
|
+
)}
|
|
959
|
+
{!fetching && !fetchError && availableModels.length > 0 && (
|
|
960
|
+
<>
|
|
961
|
+
<div className="flex items-center justify-between">
|
|
962
|
+
<span className="text-sm text-gray-400">
|
|
963
|
+
{t("providers_models.selected_count", String(fetchSelected.size), String(availableModels.length))}
|
|
964
|
+
</span>
|
|
965
|
+
<button onClick={toggleAll} className="rounded-md px-2 py-1 text-xs">
|
|
966
|
+
{allSelected
|
|
967
|
+
? <span className="text-amber-400">{t("providers_models.deselect_all")}</span>
|
|
968
|
+
: <span className="text-emerald-400">{t("providers_models.select_all")}</span>
|
|
969
|
+
}
|
|
970
|
+
</button>
|
|
971
|
+
</div>
|
|
972
|
+
<div className="max-h-72 overflow-y-auto space-y-1.5 rounded-lg border border-gray-800 p-3">
|
|
973
|
+
{availableModels.map((m) => (
|
|
974
|
+
<label key={m.id} className="flex items-center gap-3 rounded-lg px-2 py-2 transition-colors hover:bg-gray-800 cursor-pointer">
|
|
975
|
+
<div className="flex h-5 w-5 shrink-0 items-center justify-center rounded border"
|
|
976
|
+
style={{ backgroundColor: isSelected(m.id) ? "#3b82f6" : "transparent", borderColor: isSelected(m.id) ? "#3b82f6" : "#4b5563" }}
|
|
977
|
+
>
|
|
978
|
+
{isSelected(m.id) && <SquareCheck className="h-4 w-4 text-white" />}
|
|
979
|
+
</div>
|
|
980
|
+
<input type="checkbox" checked={isSelected(m.id)} onChange={() => toggleSelect(m.id)} className="sr-only" />
|
|
981
|
+
<span className="min-w-0 flex-1 truncate font-mono text-sm text-gray-200">{m.id}</span>
|
|
982
|
+
<span className={`flex shrink-0 items-center gap-1 rounded border px-1.5 py-0.5 text-xs ${m.vision ? "border-blue-500/40 bg-blue-500/10 text-blue-400" : "border-gray-700 bg-gray-800 text-gray-500"}`}>
|
|
983
|
+
{m.vision && <ImageIcon className="h-3 w-3" />}
|
|
984
|
+
{m.vision ? t("providers_models.modality_vision") : t("providers_models.modality_text")}
|
|
985
|
+
</span>
|
|
986
|
+
{m.contextWindow && <span className="rounded border border-gray-700 bg-gray-800 px-1.5 py-0.5 text-xs text-gray-500 font-mono">{formatTokens(m.contextWindow)}</span>}
|
|
987
|
+
{m.maxTokens && <span className="rounded border border-gray-700 bg-gray-800 px-1.5 py-0.5 text-xs text-gray-500 font-mono">{formatTokens(m.maxTokens)}</span>}
|
|
988
|
+
</label>
|
|
989
|
+
))}
|
|
990
|
+
</div>
|
|
991
|
+
</>
|
|
992
|
+
)}
|
|
993
|
+
<div className="flex items-center justify-end gap-3 pt-1">
|
|
994
|
+
{fetchImported !== null && (
|
|
995
|
+
<span className="flex items-center gap-1 text-sm text-emerald-400">
|
|
996
|
+
<Check className="h-4 w-4" />
|
|
997
|
+
{t("providers_models.import_done", String(fetchImported))}
|
|
998
|
+
</span>
|
|
999
|
+
)}
|
|
1000
|
+
<button onClick={() => { setFetchOpen(false); setFetchedModels([]); setFetchSelected(new Set()); setFetchError(null); setFetchImported(null); }}
|
|
1001
|
+
className="rounded-lg px-4 py-2 text-sm text-gray-400 hover:bg-gray-800">
|
|
1002
|
+
{t("models.cancel")}
|
|
1003
|
+
</button>
|
|
1004
|
+
<button onClick={handleImportFetched} disabled={fetching || fetchSelected.size === 0}
|
|
1005
|
+
className="flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50"
|
|
1006
|
+
style={{ backgroundColor: "#3b82f6", color: "#ffffff" }}>
|
|
1007
|
+
{t("providers_models.confirm_import")}
|
|
1008
|
+
</button>
|
|
1009
|
+
</div>
|
|
1010
|
+
</div>
|
|
1011
|
+
</Modal>
|
|
1012
|
+
|
|
1013
|
+
{/* Edit Model Modal */}
|
|
1014
|
+
<Modal
|
|
1015
|
+
open={!!editModel}
|
|
1016
|
+
onClose={() => setEditModel(null)}
|
|
1017
|
+
title={`${t("models.edit_model")}: ${editModel?.name || editModel?.id}`}
|
|
1018
|
+
size="lg"
|
|
1019
|
+
>
|
|
1020
|
+
{editModel && (
|
|
1021
|
+
<ModelForm
|
|
1022
|
+
initial={editModel}
|
|
1023
|
+
onSubmit={(form) => {
|
|
1024
|
+
updateModel(provider.id, editModel.id, form);
|
|
1025
|
+
setEditModel(null);
|
|
1026
|
+
}}
|
|
1027
|
+
onCancel={() => setEditModel(null)}
|
|
1028
|
+
/>
|
|
1029
|
+
)}
|
|
1030
|
+
</Modal>
|
|
1031
|
+
|
|
1032
|
+
{/* Add Model Modal */}
|
|
1033
|
+
<Modal
|
|
1034
|
+
open={showAddModel}
|
|
1035
|
+
onClose={() => setShowAddModel(false)}
|
|
1036
|
+
title={`${t("models.add_model")} — ${provider.name}`}
|
|
1037
|
+
size="lg"
|
|
1038
|
+
>
|
|
1039
|
+
<ModelForm
|
|
1040
|
+
onSubmit={(form) => {
|
|
1041
|
+
if (!form.id) return;
|
|
1042
|
+
addModel(provider.id, form as Model);
|
|
1043
|
+
setShowAddModel(false);
|
|
1044
|
+
}}
|
|
1045
|
+
onCancel={() => setShowAddModel(false)}
|
|
1046
|
+
/>
|
|
1047
|
+
</Modal>
|
|
1048
|
+
|
|
1049
|
+
{/* Delete Model Confirmation Modal */}
|
|
1050
|
+
<Modal
|
|
1051
|
+
open={!!deleteModel}
|
|
1052
|
+
onClose={() => setDeleteModel(null)}
|
|
1053
|
+
title={t("models.delete_model")}
|
|
1054
|
+
>
|
|
1055
|
+
<div className="space-y-4">
|
|
1056
|
+
<div className="flex items-start gap-3">
|
|
1057
|
+
<Trash2 className="h-5 w-5 shrink-0 mt-0.5 text-red-400" />
|
|
1058
|
+
<p className="text-sm text-gray-200">
|
|
1059
|
+
<strong className="font-mono">{deleteModel?.id}</strong>
|
|
1060
|
+
{" — "}
|
|
1061
|
+
{t("models.delete_confirm")}
|
|
1062
|
+
</p>
|
|
1063
|
+
</div>
|
|
1064
|
+
<div className="flex justify-end gap-3">
|
|
1065
|
+
<button
|
|
1066
|
+
onClick={() => setDeleteModel(null)}
|
|
1067
|
+
className="rounded-lg px-4 py-2 text-sm text-gray-400 hover:bg-gray-800"
|
|
1068
|
+
>
|
|
1069
|
+
{t("models.cancel")}
|
|
1070
|
+
</button>
|
|
1071
|
+
<button
|
|
1072
|
+
onClick={() => {
|
|
1073
|
+
if (deleteModel) {
|
|
1074
|
+
removeModel(provider.id, deleteModel.id);
|
|
1075
|
+
setDeleteModel(null);
|
|
1076
|
+
}
|
|
1077
|
+
}}
|
|
1078
|
+
className="flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white"
|
|
1079
|
+
style={{ backgroundColor: "#dc2626" }}
|
|
1080
|
+
>
|
|
1081
|
+
<Trash2 className="h-4 w-4" />
|
|
1082
|
+
{t("models.delete_model")}
|
|
1083
|
+
</button>
|
|
1084
|
+
</div>
|
|
1085
|
+
</div>
|
|
1086
|
+
</Modal>
|
|
1087
|
+
</div>
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// ─── Model Form (add & edit) ──────────────────────────────
|
|
1092
|
+
|
|
1093
|
+
function ModelForm({
|
|
1094
|
+
initial,
|
|
1095
|
+
onSubmit,
|
|
1096
|
+
onCancel,
|
|
1097
|
+
}: {
|
|
1098
|
+
initial?: Model;
|
|
1099
|
+
onSubmit: (form: Partial<Model>) => void;
|
|
1100
|
+
onCancel: () => void;
|
|
1101
|
+
}) {
|
|
1102
|
+
const { t } = useTranslation();
|
|
1103
|
+
const [form, setForm] = useState<Partial<Model>>(
|
|
1104
|
+
initial
|
|
1105
|
+
? { ...initial }
|
|
1106
|
+
: {
|
|
1107
|
+
id: "",
|
|
1108
|
+
name: "",
|
|
1109
|
+
reasoning: false,
|
|
1110
|
+
input: ["text"],
|
|
1111
|
+
contextWindow: DEFAULT_CONTEXT_WINDOW,
|
|
1112
|
+
maxTokens: DEFAULT_MAX_TOKENS,
|
|
1113
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
1114
|
+
}
|
|
1115
|
+
);
|
|
1116
|
+
const isEdit = !!initial;
|
|
1117
|
+
|
|
1118
|
+
return (
|
|
1119
|
+
<div className="space-y-4">
|
|
1120
|
+
<div className="grid grid-cols-2 gap-4">
|
|
1121
|
+
<div>
|
|
1122
|
+
<label className="block text-xs font-medium text-gray-400">{t("models.model_id")} *</label>
|
|
1123
|
+
<input
|
|
1124
|
+
type="text"
|
|
1125
|
+
value={form.id ?? ""}
|
|
1126
|
+
disabled={isEdit}
|
|
1127
|
+
onChange={(e) => setForm({ ...form, id: e.target.value })}
|
|
1128
|
+
placeholder="my-model-id"
|
|
1129
|
+
className="mt-1 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2 text-sm text-white disabled:opacity-50"
|
|
1130
|
+
/>
|
|
1131
|
+
</div>
|
|
1132
|
+
<div>
|
|
1133
|
+
<label className="block text-xs font-medium text-gray-400">{t("models.display_name")}</label>
|
|
1134
|
+
<input
|
|
1135
|
+
type="text"
|
|
1136
|
+
value={form.name ?? ""}
|
|
1137
|
+
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
|
1138
|
+
placeholder="My Custom Model"
|
|
1139
|
+
className="mt-1 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2 text-sm text-white"
|
|
1140
|
+
/>
|
|
1141
|
+
</div>
|
|
1142
|
+
<div>
|
|
1143
|
+
<label className="block text-xs font-medium text-gray-400">{t("models.context_window")}</label>
|
|
1144
|
+
<input
|
|
1145
|
+
type="number"
|
|
1146
|
+
value={form.contextWindow ?? DEFAULT_CONTEXT_WINDOW}
|
|
1147
|
+
onChange={(e) => setForm({ ...form, contextWindow: parseInt(e.target.value) || DEFAULT_CONTEXT_WINDOW })}
|
|
1148
|
+
className="mt-1 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2 text-sm text-white"
|
|
1149
|
+
/>
|
|
1150
|
+
</div>
|
|
1151
|
+
<div>
|
|
1152
|
+
<label className="block text-xs font-medium text-gray-400">{t("models.max_tokens")}</label>
|
|
1153
|
+
<input
|
|
1154
|
+
type="number"
|
|
1155
|
+
value={form.maxTokens ?? DEFAULT_MAX_TOKENS}
|
|
1156
|
+
onChange={(e) => setForm({ ...form, maxTokens: parseInt(e.target.value) || DEFAULT_MAX_TOKENS })}
|
|
1157
|
+
className="mt-1 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2 text-sm text-white"
|
|
1158
|
+
/>
|
|
1159
|
+
</div>
|
|
1160
|
+
</div>
|
|
1161
|
+
|
|
1162
|
+
{/* Capabilities */}
|
|
1163
|
+
<div className="flex flex-wrap gap-3">
|
|
1164
|
+
<label className="flex items-center gap-2 text-sm text-gray-300">
|
|
1165
|
+
<input
|
|
1166
|
+
type="checkbox"
|
|
1167
|
+
checked={form.reasoning ?? false}
|
|
1168
|
+
onChange={(e) => setForm({ ...form, reasoning: e.target.checked })}
|
|
1169
|
+
className="rounded border-gray-600 bg-gray-800 text-blue-500"
|
|
1170
|
+
/>
|
|
1171
|
+
{t("models.reasoning")}
|
|
1172
|
+
</label>
|
|
1173
|
+
<label className="flex items-center gap-2 text-sm text-gray-300">
|
|
1174
|
+
<input
|
|
1175
|
+
type="checkbox"
|
|
1176
|
+
checked={form.input?.includes("image") ?? false}
|
|
1177
|
+
onChange={(e) =>
|
|
1178
|
+
setForm({ ...form, input: e.target.checked ? ["text", "image"] : ["text"] })
|
|
1179
|
+
}
|
|
1180
|
+
className="rounded border-gray-600 bg-gray-800 text-blue-500"
|
|
1181
|
+
/>
|
|
1182
|
+
{t("models.image_input")}
|
|
1183
|
+
</label>
|
|
1184
|
+
</div>
|
|
1185
|
+
|
|
1186
|
+
{/* Cost */}
|
|
1187
|
+
<div className="grid grid-cols-4 gap-3">
|
|
1188
|
+
{(
|
|
1189
|
+
[
|
|
1190
|
+
["input", "models.cost_input"],
|
|
1191
|
+
["output", "models.cost_output"],
|
|
1192
|
+
["cacheRead", "models.cost_cache_read"],
|
|
1193
|
+
["cacheWrite", "models.cost_cache_write"],
|
|
1194
|
+
] as const
|
|
1195
|
+
).map(([field, labelKey]) => (
|
|
1196
|
+
<div key={field}>
|
|
1197
|
+
<label className="block text-xs text-gray-500">{t(labelKey)} $/M</label>
|
|
1198
|
+
<input
|
|
1199
|
+
type="number"
|
|
1200
|
+
step="0.01"
|
|
1201
|
+
value={form.cost?.[field] ?? 0}
|
|
1202
|
+
onChange={(e) =>
|
|
1203
|
+
setForm({
|
|
1204
|
+
...form,
|
|
1205
|
+
cost: {
|
|
1206
|
+
input: 0, output: 0, cacheRead: 0, cacheWrite: 0,
|
|
1207
|
+
...form.cost,
|
|
1208
|
+
[field]: parseFloat(e.target.value) || 0,
|
|
1209
|
+
},
|
|
1210
|
+
})
|
|
1211
|
+
}
|
|
1212
|
+
className="mt-1 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2 text-sm text-white"
|
|
1213
|
+
/>
|
|
1214
|
+
</div>
|
|
1215
|
+
))}
|
|
1216
|
+
</div>
|
|
1217
|
+
|
|
1218
|
+
<div className="flex justify-end gap-3 pt-2">
|
|
1219
|
+
<button onClick={onCancel} className="rounded-lg px-4 py-2 text-sm text-gray-400 hover:bg-gray-800">
|
|
1220
|
+
{t("models.cancel")}
|
|
1221
|
+
</button>
|
|
1222
|
+
<button
|
|
1223
|
+
onClick={() => onSubmit(form)}
|
|
1224
|
+
disabled={!form.id}
|
|
1225
|
+
className="rounded-lg px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50"
|
|
1226
|
+
style={{ backgroundColor: "#3b82f6", color: "#ffffff" }}
|
|
1227
|
+
>
|
|
1228
|
+
{isEdit ? t("models.save") : t("models.add_model")}
|
|
1229
|
+
</button>
|
|
1230
|
+
</div>
|
|
1231
|
+
</div>
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// ─── Add Provider Form ────────────────────────────────────
|
|
1236
|
+
|
|
1237
|
+
function AddProviderForm({
|
|
1238
|
+
onSubmit,
|
|
1239
|
+
onCancel,
|
|
1240
|
+
}: {
|
|
1241
|
+
onSubmit: (id: string, cfg: CustomProviderConfig) => Promise<boolean>;
|
|
1242
|
+
onCancel: () => void;
|
|
1243
|
+
}) {
|
|
1244
|
+
const { t } = useTranslation();
|
|
1245
|
+
const { allProviders } = useConfigStore();
|
|
1246
|
+
const [name, setName] = useState("");
|
|
1247
|
+
const [baseUrl, setBaseUrl] = useState("");
|
|
1248
|
+
const [apiKey, setApiKey] = useState("");
|
|
1249
|
+
const [api, setApi] = useState<ApiType>("openai-completions");
|
|
1250
|
+
const [models, setModels] = useState<Model[]>([]);
|
|
1251
|
+
const [showAddModel, setShowAddModel] = useState(false);
|
|
1252
|
+
const [submitting, setSubmitting] = useState(false);
|
|
1253
|
+
const [submitError, setSubmitError] = useState(false);
|
|
1254
|
+
|
|
1255
|
+
const id = deriveProviderId(name, baseUrl);
|
|
1256
|
+
const idExists = !!id && allProviders.some((p) => p.id === id);
|
|
1257
|
+
const urlInvalid = baseUrl.trim() !== "" && !isValidHttpUrl(baseUrl.trim());
|
|
1258
|
+
|
|
1259
|
+
const handleSubmit = async () => {
|
|
1260
|
+
if (!id || !baseUrl || idExists || urlInvalid) return;
|
|
1261
|
+
setSubmitting(true);
|
|
1262
|
+
setSubmitError(false);
|
|
1263
|
+
const ok = await onSubmit(id, {
|
|
1264
|
+
name: name.trim() || undefined,
|
|
1265
|
+
baseUrl,
|
|
1266
|
+
api,
|
|
1267
|
+
apiKey: apiKey || undefined,
|
|
1268
|
+
models,
|
|
1269
|
+
});
|
|
1270
|
+
setSubmitting(false);
|
|
1271
|
+
if (!ok) setSubmitError(true);
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1274
|
+
return (
|
|
1275
|
+
<div className="space-y-5">
|
|
1276
|
+
<div>
|
|
1277
|
+
<h2 className="text-lg font-semibold text-white">
|
|
1278
|
+
{t("providers_models.add_provider_title")}
|
|
1279
|
+
</h2>
|
|
1280
|
+
<p className="mt-1 text-sm text-gray-500">{t("providers_models.add_provider_desc")}</p>
|
|
1281
|
+
</div>
|
|
1282
|
+
|
|
1283
|
+
{/* Name */}
|
|
1284
|
+
<div>
|
|
1285
|
+
<label className="block text-sm text-gray-400">{t("providers_models.name")}</label>
|
|
1286
|
+
<input
|
|
1287
|
+
type="text"
|
|
1288
|
+
value={name}
|
|
1289
|
+
onChange={(e) => setName(e.target.value)}
|
|
1290
|
+
placeholder={t("providers_models.name_placeholder")}
|
|
1291
|
+
className={cn(
|
|
1292
|
+
"mt-1.5 w-full rounded-lg border bg-gray-800 px-3 py-2.5 text-sm text-white",
|
|
1293
|
+
idExists ? "border-red-500" : "border-gray-700"
|
|
1294
|
+
)}
|
|
1295
|
+
/>
|
|
1296
|
+
{idExists ? (
|
|
1297
|
+
<p className="mt-1 text-xs text-red-400">{t("providers_models.id_exists", id)}</p>
|
|
1298
|
+
) : id ? (
|
|
1299
|
+
<p className="mt-1 text-xs text-gray-500">{t("providers_models.id_preview", id)}</p>
|
|
1300
|
+
) : name.trim() ? (
|
|
1301
|
+
<p className="mt-1 text-xs text-red-400">{t("providers_models.id_invalid")}</p>
|
|
1302
|
+
) : null}
|
|
1303
|
+
</div>
|
|
1304
|
+
|
|
1305
|
+
{/* Base URL */}
|
|
1306
|
+
<div>
|
|
1307
|
+
<label className="block text-sm text-gray-400">{t("providers.base_url")}</label>
|
|
1308
|
+
<input
|
|
1309
|
+
type="text"
|
|
1310
|
+
value={baseUrl}
|
|
1311
|
+
onChange={(e) => setBaseUrl(e.target.value)}
|
|
1312
|
+
placeholder="https://api.example.com/v1"
|
|
1313
|
+
className={cn(
|
|
1314
|
+
"mt-1.5 w-full rounded-lg border bg-gray-800 px-3 py-2.5 text-sm text-white",
|
|
1315
|
+
urlInvalid ? "border-red-500" : "border-gray-700"
|
|
1316
|
+
)}
|
|
1317
|
+
/>
|
|
1318
|
+
{urlInvalid && (
|
|
1319
|
+
<p className="mt-1 text-xs text-red-400">{t("providers_models.invalid_url")}</p>
|
|
1320
|
+
)}
|
|
1321
|
+
</div>
|
|
1322
|
+
|
|
1323
|
+
{/* API Key */}
|
|
1324
|
+
<div>
|
|
1325
|
+
<label className="block text-sm text-gray-400">{t("providers.api_key")}</label>
|
|
1326
|
+
<input
|
|
1327
|
+
type="password"
|
|
1328
|
+
value={apiKey}
|
|
1329
|
+
onChange={(e) => setApiKey(e.target.value)}
|
|
1330
|
+
placeholder="sk-... or $MY_API_KEY"
|
|
1331
|
+
className="mt-1.5 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2.5 text-sm text-white"
|
|
1332
|
+
/>
|
|
1333
|
+
</div>
|
|
1334
|
+
|
|
1335
|
+
{/* API Type */}
|
|
1336
|
+
<div>
|
|
1337
|
+
<label className="block text-sm text-gray-400">{t("providers.api_type")}</label>
|
|
1338
|
+
<select
|
|
1339
|
+
value={api}
|
|
1340
|
+
onChange={(e) => setApi(e.target.value as ApiType)}
|
|
1341
|
+
className="mt-1.5 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2.5 text-sm text-white"
|
|
1342
|
+
>
|
|
1343
|
+
{API_TYPES.map((a) => (
|
|
1344
|
+
<option key={a.value} value={a.value}>{a.label}</option>
|
|
1345
|
+
))}
|
|
1346
|
+
</select>
|
|
1347
|
+
</div>
|
|
1348
|
+
|
|
1349
|
+
{/* Connection test with the values entered above */}
|
|
1350
|
+
{baseUrl.trim() !== "" && !urlInvalid && (
|
|
1351
|
+
<TestConnectionButton baseUrl={baseUrl} apiKey={apiKey} />
|
|
1352
|
+
)}
|
|
1353
|
+
|
|
1354
|
+
{/* Initial Model List */}
|
|
1355
|
+
<div>
|
|
1356
|
+
<label className="block text-sm text-gray-400">{t("providers_models.model_list")}</label>
|
|
1357
|
+
{models.length > 0 && (
|
|
1358
|
+
<div className="mt-1.5 space-y-2">
|
|
1359
|
+
{models.map((m) => (
|
|
1360
|
+
<div
|
|
1361
|
+
key={m.id}
|
|
1362
|
+
className="flex items-center gap-2 rounded-lg border border-gray-700 bg-gray-900/70 px-3 py-2.5"
|
|
1363
|
+
>
|
|
1364
|
+
<Box className="h-4 w-4 shrink-0 text-gray-500" />
|
|
1365
|
+
<span className="min-w-0 flex-1 truncate font-mono text-sm text-gray-200">
|
|
1366
|
+
{m.id}
|
|
1367
|
+
</span>
|
|
1368
|
+
<span className="rounded-md border border-gray-600 bg-gray-800/50 px-2 py-0.5 text-xs text-gray-400">
|
|
1369
|
+
{m.contextWindow ? formatTokens(m.contextWindow) : "—"}
|
|
1370
|
+
</span>
|
|
1371
|
+
<button
|
|
1372
|
+
onClick={() => setModels(models.filter((x) => x.id !== m.id))}
|
|
1373
|
+
className="rounded-md p-1.5 text-gray-500 transition-colors hover:bg-red-500/10 hover:text-red-400"
|
|
1374
|
+
title={t("models.delete_model")}
|
|
1375
|
+
>
|
|
1376
|
+
<Trash2 className="h-3.5 w-3.5" />
|
|
1377
|
+
</button>
|
|
1378
|
+
</div>
|
|
1379
|
+
))}
|
|
1380
|
+
</div>
|
|
1381
|
+
)}
|
|
1382
|
+
<button
|
|
1383
|
+
onClick={() => setShowAddModel(true)}
|
|
1384
|
+
className="mt-2 flex items-center gap-2 rounded-lg border border-gray-700 px-4 py-2 text-sm text-gray-300 transition-colors hover:bg-gray-800"
|
|
1385
|
+
>
|
|
1386
|
+
<Plus className="h-4 w-4" />
|
|
1387
|
+
{t("models.add_model")}
|
|
1388
|
+
</button>
|
|
1389
|
+
</div>
|
|
1390
|
+
|
|
1391
|
+
<div className="flex items-center gap-3">
|
|
1392
|
+
<button
|
|
1393
|
+
onClick={handleSubmit}
|
|
1394
|
+
disabled={!id || !baseUrl || idExists || urlInvalid || submitting}
|
|
1395
|
+
className="flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50"
|
|
1396
|
+
style={{ backgroundColor: "#3b82f6", color: "#ffffff" }}
|
|
1397
|
+
>
|
|
1398
|
+
{submitting && <Loader2 className="h-4 w-4 animate-spin" />}
|
|
1399
|
+
{t("providers.add_provider")}
|
|
1400
|
+
</button>
|
|
1401
|
+
<button
|
|
1402
|
+
onClick={onCancel}
|
|
1403
|
+
className="rounded-lg px-4 py-2 text-sm text-gray-400 hover:bg-gray-800"
|
|
1404
|
+
>
|
|
1405
|
+
{t("models.cancel")}
|
|
1406
|
+
</button>
|
|
1407
|
+
{submitError && (
|
|
1408
|
+
<span className="flex items-center gap-1 text-sm text-red-400">
|
|
1409
|
+
<X className="h-4 w-4" />
|
|
1410
|
+
{t("providers_models.save_failed")}
|
|
1411
|
+
</span>
|
|
1412
|
+
)}
|
|
1413
|
+
</div>
|
|
1414
|
+
|
|
1415
|
+
{/* Add Initial Model Modal */}
|
|
1416
|
+
<Modal
|
|
1417
|
+
open={showAddModel}
|
|
1418
|
+
onClose={() => setShowAddModel(false)}
|
|
1419
|
+
title={t("models.add_model")}
|
|
1420
|
+
size="lg"
|
|
1421
|
+
>
|
|
1422
|
+
<ModelForm
|
|
1423
|
+
onSubmit={(form) => {
|
|
1424
|
+
if (!form.id) return;
|
|
1425
|
+
setModels([...models.filter((x) => x.id !== form.id), form as Model]);
|
|
1426
|
+
setShowAddModel(false);
|
|
1427
|
+
}}
|
|
1428
|
+
onCancel={() => setShowAddModel(false)}
|
|
1429
|
+
/>
|
|
1430
|
+
</Modal>
|
|
1431
|
+
</div>
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// ─── Import Provider Modal ─────────────────────────────────
|
|
1436
|
+
|
|
1437
|
+
function ImportProviderModal({
|
|
1438
|
+
open,
|
|
1439
|
+
onClose,
|
|
1440
|
+
onImported,
|
|
1441
|
+
}: {
|
|
1442
|
+
open: boolean;
|
|
1443
|
+
onClose: () => void;
|
|
1444
|
+
onImported: (id: string) => void;
|
|
1445
|
+
}) {
|
|
1446
|
+
const { t } = useTranslation();
|
|
1447
|
+
const { allProviders } = useConfigStore();
|
|
1448
|
+
|
|
1449
|
+
const [text, setText] = useState("");
|
|
1450
|
+
const [name, setName] = useState("");
|
|
1451
|
+
const [baseUrl, setBaseUrl] = useState("");
|
|
1452
|
+
const [apiKey, setApiKey] = useState("");
|
|
1453
|
+
const [api, setApi] = useState<ApiType>("openai-completions");
|
|
1454
|
+
const [modelIds, setModelIds] = useState<string[]>([]);
|
|
1455
|
+
const [submitting, setSubmitting] = useState(false);
|
|
1456
|
+
const [submitError, setSubmitError] = useState(false);
|
|
1457
|
+
|
|
1458
|
+
// ─── Fetch models from the endpoint (one-shot import) ───
|
|
1459
|
+
const [fetching, setFetching] = useState(false);
|
|
1460
|
+
const [fetchErr, setFetchErr] = useState<string | null>(null);
|
|
1461
|
+
const [fetchDone, setFetchDone] = useState(false);
|
|
1462
|
+
const [fetchedModels, setFetchedModels] = useState<{ id: string; contextWindow?: number; maxTokens?: number; vision?: boolean }[]>([]);
|
|
1463
|
+
const [fetchSel, setFetchSel] = useState<Set<string>>(new Set());
|
|
1464
|
+
|
|
1465
|
+
// Re-parse on every paste/edit of the raw text; fields below stay editable
|
|
1466
|
+
const handleText = (value: string) => {
|
|
1467
|
+
setText(value);
|
|
1468
|
+
const parsed = parseProviderImport(value);
|
|
1469
|
+
setName(parsed.name);
|
|
1470
|
+
setBaseUrl(parsed.baseUrl);
|
|
1471
|
+
setApiKey(parsed.apiKey);
|
|
1472
|
+
setModelIds(parsed.modelIds);
|
|
1473
|
+
};
|
|
1474
|
+
|
|
1475
|
+
const reset = () => {
|
|
1476
|
+
setText("");
|
|
1477
|
+
setName("");
|
|
1478
|
+
setBaseUrl("");
|
|
1479
|
+
setApiKey("");
|
|
1480
|
+
setApi("openai-completions");
|
|
1481
|
+
setModelIds([]);
|
|
1482
|
+
setSubmitting(false);
|
|
1483
|
+
setSubmitError(false);
|
|
1484
|
+
setFetching(false);
|
|
1485
|
+
setFetchErr(null);
|
|
1486
|
+
setFetchDone(false);
|
|
1487
|
+
setFetchedModels([]);
|
|
1488
|
+
setFetchSel(new Set());
|
|
1489
|
+
};
|
|
1490
|
+
|
|
1491
|
+
const id = deriveProviderId(name, baseUrl);
|
|
1492
|
+
const existing = allProviders.find((p) => p.id === id);
|
|
1493
|
+
const builtinConflict = existing?.type === "builtin";
|
|
1494
|
+
const mergeTarget = existing?.type === "custom" ? existing : null;
|
|
1495
|
+
const urlInvalid = baseUrl.trim() !== "" && !isValidHttpUrl(baseUrl.trim());
|
|
1496
|
+
const parsedEmpty =
|
|
1497
|
+
text.trim() !== "" && !name && !baseUrl && !apiKey && modelIds.length === 0;
|
|
1498
|
+
const canSubmit =
|
|
1499
|
+
!!id && (!!baseUrl.trim() || !!mergeTarget) && !urlInvalid && !builtinConflict && !submitting;
|
|
1500
|
+
|
|
1501
|
+
// Fetched models not already covered by the parsed model chips
|
|
1502
|
+
const availableFetched = fetchedModels.filter((m) => !modelIds.includes(m.id));
|
|
1503
|
+
const allFetchedSelected =
|
|
1504
|
+
availableFetched.length > 0 && availableFetched.every((m) => fetchSel.has(m.id));
|
|
1505
|
+
|
|
1506
|
+
const handleFetchModels = async () => {
|
|
1507
|
+
if (!isValidHttpUrl(baseUrl.trim()) || fetching) return;
|
|
1508
|
+
setFetching(true);
|
|
1509
|
+
setFetchErr(null);
|
|
1510
|
+
setFetchDone(false);
|
|
1511
|
+
try {
|
|
1512
|
+
const res = await fetch("/api/pi/provider-models", {
|
|
1513
|
+
method: "POST",
|
|
1514
|
+
headers: { "Content-Type": "application/json" },
|
|
1515
|
+
body: JSON.stringify({ baseUrl: baseUrl.trim(), apiKey, providerId: id || undefined }),
|
|
1516
|
+
});
|
|
1517
|
+
const data = await res.json();
|
|
1518
|
+
if (data.error) {
|
|
1519
|
+
setFetchErr(data.error);
|
|
1520
|
+
} else {
|
|
1521
|
+
const models = (data.models ?? []) as { id: string; contextWindow?: number; maxTokens?: number; vision?: boolean }[];
|
|
1522
|
+
setFetchedModels(models);
|
|
1523
|
+
// Select everything by default so import is a single click
|
|
1524
|
+
setFetchSel(new Set(models.map((m) => m.id)));
|
|
1525
|
+
}
|
|
1526
|
+
} catch {
|
|
1527
|
+
setFetchErr("network error");
|
|
1528
|
+
} finally {
|
|
1529
|
+
setFetching(false);
|
|
1530
|
+
setFetchDone(true);
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1534
|
+
const toggleFetched = (mid: string) => {
|
|
1535
|
+
setFetchSel((prev) => {
|
|
1536
|
+
const next = new Set(prev);
|
|
1537
|
+
next.has(mid) ? next.delete(mid) : next.add(mid);
|
|
1538
|
+
return next;
|
|
1539
|
+
});
|
|
1540
|
+
};
|
|
1541
|
+
const toggleAllFetched = () => {
|
|
1542
|
+
setFetchSel(allFetchedSelected ? new Set() : new Set(availableFetched.map((m) => m.id)));
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
const handleImport = async () => {
|
|
1546
|
+
if (!canSubmit) return;
|
|
1547
|
+
setSubmitting(true);
|
|
1548
|
+
setSubmitError(false);
|
|
1549
|
+
const store = useConfigStore.getState();
|
|
1550
|
+
const defaults = {
|
|
1551
|
+
input: ["text"] as Model["input"],
|
|
1552
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
1553
|
+
};
|
|
1554
|
+
const selectedFetched = availableFetched.filter((m) => fetchSel.has(m.id));
|
|
1555
|
+
const newModels: Model[] = [
|
|
1556
|
+
...modelIds.map((mid) => ({
|
|
1557
|
+
id: mid,
|
|
1558
|
+
name: mid.split("/").pop() || mid,
|
|
1559
|
+
contextWindow: DEFAULT_CONTEXT_WINDOW,
|
|
1560
|
+
maxTokens: DEFAULT_MAX_TOKENS,
|
|
1561
|
+
...defaults,
|
|
1562
|
+
})),
|
|
1563
|
+
// Fetched models carry real context/output limits when the endpoint provides them
|
|
1564
|
+
...selectedFetched.map((m) => ({
|
|
1565
|
+
id: m.id,
|
|
1566
|
+
name: m.id.split("/").pop() || m.id,
|
|
1567
|
+
contextWindow: m.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
|
|
1568
|
+
maxTokens: m.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
1569
|
+
...defaults,
|
|
1570
|
+
input: (m.vision ? ["text", "image"] : ["text"]) as Model["input"],
|
|
1571
|
+
})),
|
|
1572
|
+
];
|
|
1573
|
+
const newIds = newModels.map((m) => m.id);
|
|
1574
|
+
|
|
1575
|
+
let ok: boolean;
|
|
1576
|
+
if (mergeTarget) {
|
|
1577
|
+
// Merge into the existing custom provider (dedupe models by id)
|
|
1578
|
+
const existingCfg = store.modelsJson?.providers[id];
|
|
1579
|
+
const existingModels = existingCfg?.models ?? [];
|
|
1580
|
+
const merged = [
|
|
1581
|
+
...existingModels,
|
|
1582
|
+
...newModels.filter((m) => !existingModels.some((e) => e.id === m.id)),
|
|
1583
|
+
];
|
|
1584
|
+
ok = await store.updateCustomProvider(id, {
|
|
1585
|
+
baseUrl: baseUrl.trim() || existingCfg?.baseUrl,
|
|
1586
|
+
apiKey: apiKey || existingCfg?.apiKey,
|
|
1587
|
+
models: merged,
|
|
1588
|
+
});
|
|
1589
|
+
} else {
|
|
1590
|
+
ok = await store.addCustomProvider(id, {
|
|
1591
|
+
name: name.trim() || undefined,
|
|
1592
|
+
baseUrl: baseUrl.trim(),
|
|
1593
|
+
api,
|
|
1594
|
+
apiKey: apiKey || undefined,
|
|
1595
|
+
models: newModels,
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// Imported models are enabled by default (settings.enabledModels refs)
|
|
1600
|
+
if (ok && newIds.length > 0) {
|
|
1601
|
+
const refs = newIds.map((m) => `${id}/${m}`);
|
|
1602
|
+
const list = store.settings?.enabledModels ?? [];
|
|
1603
|
+
await store.updateSettings({ enabledModels: Array.from(new Set([...list, ...refs])) });
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
setSubmitting(false);
|
|
1607
|
+
if (ok) {
|
|
1608
|
+
reset();
|
|
1609
|
+
onImported(id);
|
|
1610
|
+
} else {
|
|
1611
|
+
setSubmitError(true);
|
|
1612
|
+
}
|
|
1613
|
+
};
|
|
1614
|
+
|
|
1615
|
+
return (
|
|
1616
|
+
<Modal
|
|
1617
|
+
open={open}
|
|
1618
|
+
onClose={() => { reset(); onClose(); }}
|
|
1619
|
+
title={t("providers_models.import_title")}
|
|
1620
|
+
size="lg"
|
|
1621
|
+
>
|
|
1622
|
+
<div className="space-y-4">
|
|
1623
|
+
<p className="text-sm text-gray-500">{t("providers_models.import_desc")}</p>
|
|
1624
|
+
|
|
1625
|
+
{/* Paste area */}
|
|
1626
|
+
<textarea
|
|
1627
|
+
value={text}
|
|
1628
|
+
onChange={(e) => handleText(e.target.value)}
|
|
1629
|
+
rows={4}
|
|
1630
|
+
placeholder={t("providers_models.import_placeholder")}
|
|
1631
|
+
className="w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2.5 font-mono text-sm text-white placeholder:text-gray-600"
|
|
1632
|
+
/>
|
|
1633
|
+
{parsedEmpty && (
|
|
1634
|
+
<p className="text-xs text-amber-400">{t("providers_models.import_empty")}</p>
|
|
1635
|
+
)}
|
|
1636
|
+
|
|
1637
|
+
{/* Parsed preview (editable) */}
|
|
1638
|
+
<div className="grid grid-cols-2 gap-4">
|
|
1639
|
+
<div>
|
|
1640
|
+
<label className="block text-xs font-medium text-gray-400">
|
|
1641
|
+
{t("providers_models.name")}
|
|
1642
|
+
</label>
|
|
1643
|
+
<input
|
|
1644
|
+
type="text"
|
|
1645
|
+
value={name}
|
|
1646
|
+
onChange={(e) => setName(e.target.value)}
|
|
1647
|
+
placeholder={t("providers_models.name_placeholder")}
|
|
1648
|
+
className={cn(
|
|
1649
|
+
"mt-1 w-full rounded-lg border bg-gray-800 px-3 py-2 text-sm text-white",
|
|
1650
|
+
builtinConflict ? "border-red-500" : "border-gray-700"
|
|
1651
|
+
)}
|
|
1652
|
+
/>
|
|
1653
|
+
{builtinConflict ? (
|
|
1654
|
+
<p className="mt-1 text-xs text-red-400">
|
|
1655
|
+
{t("providers_models.import_builtin_conflict", id)}
|
|
1656
|
+
</p>
|
|
1657
|
+
) : mergeTarget ? (
|
|
1658
|
+
<p className="mt-1 text-xs text-amber-400">
|
|
1659
|
+
{t("providers_models.import_merge", id)}
|
|
1660
|
+
</p>
|
|
1661
|
+
) : id ? (
|
|
1662
|
+
<p className="mt-1 text-xs text-gray-500">{t("providers_models.id_preview", id)}</p>
|
|
1663
|
+
) : name.trim() ? (
|
|
1664
|
+
<p className="mt-1 text-xs text-red-400">{t("providers_models.id_invalid")}</p>
|
|
1665
|
+
) : null}
|
|
1666
|
+
</div>
|
|
1667
|
+
<div>
|
|
1668
|
+
<label className="block text-xs font-medium text-gray-400">
|
|
1669
|
+
{t("providers.api_type")}
|
|
1670
|
+
</label>
|
|
1671
|
+
<select
|
|
1672
|
+
value={api}
|
|
1673
|
+
onChange={(e) => setApi(e.target.value as ApiType)}
|
|
1674
|
+
className="mt-1 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2 text-sm text-white"
|
|
1675
|
+
>
|
|
1676
|
+
{API_TYPES.map((a) => (
|
|
1677
|
+
<option key={a.value} value={a.value}>{a.label}</option>
|
|
1678
|
+
))}
|
|
1679
|
+
</select>
|
|
1680
|
+
</div>
|
|
1681
|
+
<div>
|
|
1682
|
+
<label className="block text-xs font-medium text-gray-400">
|
|
1683
|
+
{t("providers.base_url")}
|
|
1684
|
+
</label>
|
|
1685
|
+
<input
|
|
1686
|
+
type="text"
|
|
1687
|
+
value={baseUrl}
|
|
1688
|
+
onChange={(e) => setBaseUrl(e.target.value)}
|
|
1689
|
+
placeholder="https://api.example.com/v1"
|
|
1690
|
+
className={cn(
|
|
1691
|
+
"mt-1 w-full rounded-lg border bg-gray-800 px-3 py-2 text-sm text-white",
|
|
1692
|
+
urlInvalid ? "border-red-500" : "border-gray-700"
|
|
1693
|
+
)}
|
|
1694
|
+
/>
|
|
1695
|
+
{urlInvalid && (
|
|
1696
|
+
<p className="mt-1 text-xs text-red-400">{t("providers_models.invalid_url")}</p>
|
|
1697
|
+
)}
|
|
1698
|
+
</div>
|
|
1699
|
+
<div>
|
|
1700
|
+
<label className="block text-xs font-medium text-gray-400">
|
|
1701
|
+
{t("providers.api_key")}
|
|
1702
|
+
</label>
|
|
1703
|
+
<input
|
|
1704
|
+
type="password"
|
|
1705
|
+
value={apiKey}
|
|
1706
|
+
onChange={(e) => setApiKey(e.target.value)}
|
|
1707
|
+
placeholder="sk-... or $MY_API_KEY"
|
|
1708
|
+
className="mt-1 w-full rounded-lg border border-gray-700 bg-gray-800 px-3 py-2 text-sm text-white"
|
|
1709
|
+
/>
|
|
1710
|
+
</div>
|
|
1711
|
+
</div>
|
|
1712
|
+
|
|
1713
|
+
{/* Detected models */}
|
|
1714
|
+
{modelIds.length > 0 && (
|
|
1715
|
+
<div>
|
|
1716
|
+
<label className="block text-xs font-medium text-gray-400">
|
|
1717
|
+
{t("providers_models.model_list")}
|
|
1718
|
+
</label>
|
|
1719
|
+
<div className="mt-1.5 flex flex-wrap gap-2">
|
|
1720
|
+
{modelIds.map((mid) => (
|
|
1721
|
+
<span
|
|
1722
|
+
key={mid}
|
|
1723
|
+
className="flex items-center gap-1.5 rounded-md border border-gray-700 bg-gray-800 px-2 py-1 font-mono text-xs text-gray-200"
|
|
1724
|
+
>
|
|
1725
|
+
<Box className="h-3 w-3 text-gray-500" />
|
|
1726
|
+
{mid}
|
|
1727
|
+
<button
|
|
1728
|
+
onClick={() => setModelIds(modelIds.filter((x) => x !== mid))}
|
|
1729
|
+
className="text-gray-500 hover:text-red-400"
|
|
1730
|
+
>
|
|
1731
|
+
<X className="h-3 w-3" />
|
|
1732
|
+
</button>
|
|
1733
|
+
</span>
|
|
1734
|
+
))}
|
|
1735
|
+
</div>
|
|
1736
|
+
</div>
|
|
1737
|
+
)}
|
|
1738
|
+
|
|
1739
|
+
{/* Fetch models from the endpoint — one-shot: fetch, tick, import */}
|
|
1740
|
+
<div>
|
|
1741
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
1742
|
+
<button
|
|
1743
|
+
onClick={handleFetchModels}
|
|
1744
|
+
disabled={!isValidHttpUrl(baseUrl.trim()) || fetching}
|
|
1745
|
+
title={
|
|
1746
|
+
!isValidHttpUrl(baseUrl.trim())
|
|
1747
|
+
? t("providers_models.fetch_no_endpoint")
|
|
1748
|
+
: t("providers_models.fetch_models")
|
|
1749
|
+
}
|
|
1750
|
+
className="flex items-center gap-2 rounded-lg border border-gray-700 px-4 py-2 text-sm text-gray-300 transition-colors hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-50"
|
|
1751
|
+
>
|
|
1752
|
+
{fetching ? <Loader2 className="h-4 w-4 animate-spin" /> : <Download className="h-4 w-4" />}
|
|
1753
|
+
{fetching ? t("providers_models.fetching") : t("providers_models.fetch_models")}
|
|
1754
|
+
</button>
|
|
1755
|
+
{fetchErr && (
|
|
1756
|
+
<span className="text-xs text-red-400">{t("providers_models.fetch_error", fetchErr)}</span>
|
|
1757
|
+
)}
|
|
1758
|
+
{fetchDone && !fetchErr && fetchedModels.length === 0 && (
|
|
1759
|
+
<span className="text-xs text-amber-400">{t("providers_models.fetch_empty")}</span>
|
|
1760
|
+
)}
|
|
1761
|
+
</div>
|
|
1762
|
+
{availableFetched.length > 0 && (
|
|
1763
|
+
<div className="mt-2 rounded-lg border border-gray-700">
|
|
1764
|
+
<div className="flex items-center justify-between border-b border-gray-700 px-3 py-2">
|
|
1765
|
+
<span className="text-xs text-gray-400">
|
|
1766
|
+
{t("providers_models.selected_count", String(fetchSel.size), String(availableFetched.length))}
|
|
1767
|
+
</span>
|
|
1768
|
+
<button
|
|
1769
|
+
onClick={toggleAllFetched}
|
|
1770
|
+
className="text-xs text-blue-400 hover:text-blue-300"
|
|
1771
|
+
>
|
|
1772
|
+
{allFetchedSelected ? t("providers_models.deselect_all") : t("providers_models.select_all")}
|
|
1773
|
+
</button>
|
|
1774
|
+
</div>
|
|
1775
|
+
<div className="max-h-44 overflow-y-auto p-1.5">
|
|
1776
|
+
{availableFetched.map((m) => (
|
|
1777
|
+
<label
|
|
1778
|
+
key={m.id}
|
|
1779
|
+
className="flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-1.5 hover:bg-gray-800"
|
|
1780
|
+
>
|
|
1781
|
+
<input
|
|
1782
|
+
type="checkbox"
|
|
1783
|
+
checked={fetchSel.has(m.id)}
|
|
1784
|
+
onChange={() => toggleFetched(m.id)}
|
|
1785
|
+
className="h-3.5 w-3.5 accent-blue-500"
|
|
1786
|
+
/>
|
|
1787
|
+
<span className="truncate font-mono text-xs text-gray-200">{m.id}</span>
|
|
1788
|
+
<span className={`ml-auto flex shrink-0 items-center gap-1 rounded border px-1 py-px text-[10px] ${m.vision ? "border-blue-500/40 bg-blue-500/10 text-blue-400" : "border-gray-700 bg-gray-800 text-gray-500"}`}>
|
|
1789
|
+
{m.vision && <ImageIcon className="h-2.5 w-2.5" />}
|
|
1790
|
+
{m.vision ? t("providers_models.modality_vision") : t("providers_models.modality_text")}
|
|
1791
|
+
</span>
|
|
1792
|
+
{m.contextWindow && (
|
|
1793
|
+
<span className="shrink-0 text-[10px] text-gray-500">
|
|
1794
|
+
{formatTokens(m.contextWindow)}
|
|
1795
|
+
</span>
|
|
1796
|
+
)}
|
|
1797
|
+
</label>
|
|
1798
|
+
))}
|
|
1799
|
+
</div>
|
|
1800
|
+
</div>
|
|
1801
|
+
)}
|
|
1802
|
+
</div>
|
|
1803
|
+
|
|
1804
|
+
<div className="flex items-center justify-end gap-3 pt-1">
|
|
1805
|
+
{submitError && (
|
|
1806
|
+
<span className="flex items-center gap-1 text-sm text-red-400">
|
|
1807
|
+
<X className="h-4 w-4" />
|
|
1808
|
+
{t("providers_models.save_failed")}
|
|
1809
|
+
</span>
|
|
1810
|
+
)}
|
|
1811
|
+
<button
|
|
1812
|
+
onClick={() => { reset(); onClose(); }}
|
|
1813
|
+
className="rounded-lg px-4 py-2 text-sm text-gray-400 hover:bg-gray-800"
|
|
1814
|
+
>
|
|
1815
|
+
{t("models.cancel")}
|
|
1816
|
+
</button>
|
|
1817
|
+
<button
|
|
1818
|
+
onClick={handleImport}
|
|
1819
|
+
disabled={!canSubmit}
|
|
1820
|
+
className="flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50"
|
|
1821
|
+
style={{ backgroundColor: "#3b82f6", color: "#ffffff" }}
|
|
1822
|
+
>
|
|
1823
|
+
{submitting && <Loader2 className="h-4 w-4 animate-spin" />}
|
|
1824
|
+
{t("providers_models.import")}
|
|
1825
|
+
</button>
|
|
1826
|
+
</div>
|
|
1827
|
+
</div>
|
|
1828
|
+
</Modal>
|
|
1829
|
+
);
|
|
1830
|
+
}
|