sliccy 6.181.0 → 6.183.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/.vite/manifest.json +274 -266
- package/dist/ui/assets/SKILL-B4YYagrM.js +1 -0
- package/dist/ui/assets/{SKILL-BpMlcVho.js → SKILL-BbKu0tAh.js} +1 -1
- package/dist/ui/assets/{SKILL-DGelbGt_.js → SKILL-CG7Xvjv7.js} +1 -1
- package/dist/ui/assets/__vite-browser-external-CJkRLpJ-.js +1 -0
- package/dist/ui/assets/{account-store-J3ybiSNw.js → account-store-Ce44FdYj.js} +2 -2
- package/dist/ui/assets/{account-store-0RMtbmqz.js → account-store-D0ciUU3U.js} +1 -1
- package/dist/ui/assets/{adobe-YOeKKYUk.js → adobe-FgsPWBjV.js} +2 -2
- package/dist/ui/assets/{adobe-DEXpooxf.js → adobe-j1r9Po8o.js} +1 -1
- package/dist/ui/assets/{agent-panels-B4hlhzVG.js → agent-panels-DcVWQZVh.js} +1 -1
- package/dist/ui/assets/{almost-bash-shell-headless-BR1Hr6tM.js → almost-bash-shell-headless-CsDQjWSX.js} +270 -270
- package/dist/ui/assets/{apply-layout-BgFVMSBx.js → apply-layout-6-QTVrMq.js} +1 -1
- package/dist/ui/assets/{apps-oyrUmtWx.js → apps-C20mWJ8w.js} +1 -1
- package/dist/ui/assets/{azure-openai-Ytc_MWDd.js → azure-openai-CTmHX_BT.js} +1 -1
- package/dist/ui/assets/{azure-openai-B6qTFVGv.js → azure-openai-CotI4RBJ.js} +1 -1
- package/dist/ui/assets/{backend-hostfs-CEXlx0cW.js → backend-hostfs-D46_hViH.js} +1 -1
- package/dist/ui/assets/{browser-DG5MjvHF.js → browser-BM_DFzrh.js} +2 -2
- package/dist/ui/assets/{bsh-watchdog-DBtF-C4h.js → bsh-watchdog-CmaS7wa9.js} +1 -1
- package/dist/ui/assets/{capability-C8I_18AN.js → capability-BgC-ru0t.js} +2 -2
- package/dist/ui/assets/{cdp-DVWJozRc.js → cdp-BvPARz8i.js} +1 -1
- package/dist/ui/assets/{cerebras-BT44OLPt.js → cerebras-CGndqdHt.js} +1 -1
- package/dist/ui/assets/{cerebras-brHRFUH2.js → cerebras-DG7lHBt1.js} +1 -1
- package/dist/ui/assets/{client-Sb_wIqH2.js → client-CmfvXtLV.js} +1 -1
- package/dist/ui/assets/{composer-speech-BZQtz7u1.js → composer-speech-C2InwNBM.js} +1 -1
- package/dist/ui/assets/{connect-surface-D7cq2QI7.js → connect-surface-EWOA61l_.js} +1 -1
- package/dist/ui/assets/{core-B3ULTK7V.js → core-Be1yHH6v.js} +1 -1
- package/dist/ui/assets/{core-aQUekdF7.js → core-D-Fp1HL2.js} +2 -2
- package/dist/ui/assets/{cost-command-DZQVtgRS.js → cost-command-CitGzr6C.js} +1 -1
- package/dist/ui/assets/{directed-approval-DvAxEX5J.js → directed-approval-6QIzcmF_.js} +1 -1
- package/dist/ui/assets/{discovery-preference-C7kFEKRM.js → discovery-preference-MWZVi-PZ.js} +1 -1
- package/dist/ui/assets/dist-C8ZpEU8V.js +3 -0
- package/dist/ui/assets/{emscripten-vfs-hook-agmT6Hsz.js → emscripten-vfs-hook-B9KkLncp.js} +1 -1
- package/dist/ui/assets/{ensure-speech-assets-CQ05z2Xw.js → ensure-speech-assets-C3v1Nu2n.js} +1 -1
- package/dist/ui/assets/{esbuild-wasm-DS_YG2EY.js → esbuild-wasm-CeZYVyQ9.js} +1 -1
- package/dist/ui/assets/{extension-ops-CmuqbGmo.js → extension-ops-DTRUyGkx.js} +1 -1
- package/dist/ui/assets/{ffmpeg-wasm-B77gfnB_.js → ffmpeg-wasm-CV1BmGNS.js} +1 -1
- package/dist/ui/assets/{float-topology-BvkgNLoi.js → float-topology-B0_dG7En.js} +1 -1
- package/dist/ui/assets/{follower-navigate-watcher-B8Kk1WcF.js → follower-navigate-watcher-DVWQWTGU.js} +1 -1
- package/dist/ui/assets/{frozen-archive-writer-qxZ9jGOV.js → frozen-archive-writer-DMShgEqJ.js} +1 -1
- package/dist/ui/assets/{fs-Dkh-H6Cd.js → fs-JF56Nord.js} +2 -2
- package/dist/ui/assets/{fs-D6FCLu_F.js → fs-TVcPtHbB.js} +1 -1
- package/dist/ui/assets/{gelatiere-store-ChogqkEu.js → gelatiere-store-BnAzlDHe.js} +1 -1
- package/dist/ui/assets/{gelatiere-unit-DxFUh33e.js → gelatiere-unit-5cS02rOR.js} +1 -1
- package/dist/ui/assets/{github-D31l6kcK.js → github-BJKcZ0cU.js} +2 -2
- package/dist/ui/assets/{github-CrHzeKQ8.js → github-DsvjkXr7.js} +1 -1
- package/dist/ui/assets/{github-auth-VTbKhlbJ.js → github-auth-Crm1hubQ.js} +1 -1
- package/dist/ui/assets/{github-copilot-eUb4Djg_.js → github-copilot-BWhNAowY.js} +1 -1
- package/dist/ui/assets/{github-copilot-CVwHDKPD.js → github-copilot-ngj7NQ16.js} +1 -1
- package/dist/ui/assets/{github-install-Dkt1u6HU.js → github-install-DB3-4PDg.js} +1 -1
- package/dist/ui/assets/{github-zip-B6gHXxqd.js → github-zip-CfS9P2dH.js} +1 -1
- package/dist/ui/assets/{guest-tool-gate-YxkVsl15.js → guest-tool-gate-B2z1ggE_.js} +1 -1
- package/dist/ui/assets/{hear-BKQjhhNP.js → hear-akPMJ6kC.js} +1 -1
- package/dist/ui/assets/{help-C7WHd3Ia.js → help-CNHuyT-F.js} +1 -1
- package/dist/ui/assets/{host-command-ODKvqvjN.js → host-command-PUA7nfMk.js} +1 -1
- package/dist/ui/assets/{installer-D35TzRKw.js → installer-CzQ_lEMz.js} +1 -1
- package/dist/ui/assets/{interrupted-work-recovery-BK9ZDM7F.js → interrupted-work-recovery-r9fITX8G.js} +1 -1
- package/dist/ui/assets/js-realm-worker-EkjzOn2Q.js +53 -0
- package/dist/ui/assets/jsh-executor-C849KexO.js +18 -0
- package/dist/ui/assets/{kernel-worker-UUfeG-Eg.js → kernel-worker-B69_4v-D.js} +10 -10
- package/dist/ui/assets/{lick-ws-bridge-DoyPgrSr.js → lick-ws-bridge-BygSyyxa.js} +1 -1
- package/dist/ui/assets/{lick-ws-bridge-DdgwrW91.js → lick-ws-bridge-DDMdP2AB.js} +1 -1
- package/dist/ui/assets/{live-session-curation-6O9TA5qM.js → live-session-curation-BfplChNV.js} +1 -1
- package/dist/ui/assets/{live-session-snapshot-D_7T7FHe.js → live-session-snapshot-BQQH-IkA.js} +1 -1
- package/dist/ui/assets/{local-llm-BYaSa2xI.js → local-llm-BSMA7cC5.js} +1 -1
- package/dist/ui/assets/{local-llm-TK4oGrWD.js → local-llm-CNznNtp9.js} +1 -1
- package/dist/ui/assets/{magick-wasm-DCNZhAyd.js → magick-wasm-KFp97Nzb.js} +1 -1
- package/dist/ui/assets/{main-Bgkf5d3Z.js → main-BsG-jtvC.js} +3 -3
- package/dist/ui/assets/{main-cherry-BUdIAeG5.js → main-cherry-7eVbk29W.js} +1 -1
- package/dist/ui/assets/{memory-curation-seam-CNOuxuz4.js → memory-curation-seam-z19QoOG6.js} +1 -1
- package/dist/ui/assets/{memory-health-BSo0HKQx.js → memory-health-CL5kYVAr.js} +1 -1
- package/dist/ui/assets/{memory-write-execute-mQfg-aG4.js → memory-write-execute-C_oehZl_.js} +1 -1
- package/dist/ui/assets/{mount-h-dwh5GL.js → mount-BvxmwqOG.js} +3 -3
- package/dist/ui/assets/{mount-BpcQX-Sq.js → mount-JNIgSYT5.js} +1 -1
- package/dist/ui/assets/{mount-bridge-client-C4Ti1B85.js → mount-bridge-client-CaHZ1pkb.js} +1 -1
- package/dist/ui/assets/{mount-bridge-client-BMkDfg_K.js → mount-bridge-client-DRVFK3mJ.js} +1 -1
- package/dist/ui/assets/{new-session-CTx7YRbr.js → new-session-qNCtGIiI.js} +2 -2
- package/dist/ui/assets/{oauth-bootstrap-B18nfDG4.js → oauth-bootstrap-F0HvaeYA.js} +2 -2
- package/dist/ui/assets/{oauth-service-CxXBA9rC.js → oauth-service-Dn_Xz1FZ.js} +1 -1
- package/dist/ui/assets/{oauth-service-B8mWc7rY.js → oauth-service-_PUd0RqU.js} +1 -1
- package/dist/ui/assets/{onboarding-orchestrator-Df3UDgbD.js → onboarding-orchestrator-OKDMP2X_.js} +1 -1
- package/dist/ui/assets/{openai-codex-Cu1dT_JE.js → openai-codex-6hCaJG3N.js} +1 -1
- package/dist/ui/assets/{openai-codex-DBUtEEk3.js → openai-codex-BbYHsYuO.js} +1 -1
- package/dist/ui/assets/{openrouter-BwrZKpwu.js → openrouter-BPYQjzK5.js} +1 -1
- package/dist/ui/assets/{openrouter-CrB0BbnV.js → openrouter-BstowyUr.js} +1 -1
- package/dist/ui/assets/{openrouter-free-B_LRYNX8.js → openrouter-free-B1qsiSWg.js} +1 -1
- package/dist/ui/assets/{openrouter-free-CXqUunRn.js → openrouter-free-DMavbTS5.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-Bpzo9cvY.js → openrouter-oauth-CYZRlFsL.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-Bnohq7Ac.js → openrouter-oauth-DrKARZLe.js} +1 -1
- package/dist/ui/assets/{opfs-mount-CmDtZOGy.js → opfs-mount-D3x0tHeU.js} +1 -1
- package/dist/ui/assets/{page-approval-service-CYpStEUs.js → page-approval-service-DRZEjhrX.js} +1 -1
- package/dist/ui/assets/{panel-rpc-handlers-BFG-ZvfW.js → panel-rpc-handlers-CiSSOSEo.js} +2 -2
- package/dist/ui/assets/{panelize-shell-CLHSr5bz.js → panelize-shell-CILi0yPu.js} +4 -4
- package/dist/ui/assets/{pi-edit-execution-wHbJ4R48.js → pi-edit-execution-CEUeI3PX.js} +1 -1
- package/dist/ui/assets/{port-bridge-client-CrZ9vnOK.js → port-bridge-client-BiWcfZSc.js} +1 -1
- package/dist/ui/assets/{port-bridge-client-BHH6FPBY.js → port-bridge-client-BvO-1Rao.js} +1 -1
- package/dist/ui/assets/{provider-BXK9OD8Z.js → provider-CPes-oeu.js} +2 -2
- package/dist/ui/assets/{provider-DyqdvQeZ.js → provider-Dv9W3IvY.js} +1 -1
- package/dist/ui/assets/{provider-settings-BO6Fedc5.js → provider-settings-CDcPLuoa.js} +1 -1
- package/dist/ui/assets/provider-store-access-B7yFT4hY.js +1 -0
- package/dist/ui/assets/provider-store-access-D2Rj0Y-3.js +1 -0
- package/dist/ui/assets/{providers-BvApGkM9.js → providers-COWrD21n.js} +2 -2
- package/dist/ui/assets/{providers-CAcycJZb.js → providers-DLk_1jra.js} +1 -1
- package/dist/ui/assets/proxied-fetch-DiXk_XGs.js +2 -0
- package/dist/ui/assets/proxied-fetch-J19bK2dG.js +1 -0
- package/dist/ui/assets/{py-live-vfs-Di71Rd2P.js → py-live-vfs-DMc0LhCg.js} +12 -0
- package/dist/ui/assets/{py-live-vfs-CJWt_6ye.js → py-live-vfs-Dm8bBjqR.js} +12 -0
- package/dist/ui/assets/py-realm-worker-BaO89JAR.js +255 -0
- package/dist/ui/assets/{pyodide-W71AWfnt.js → pyodide-DY17DXNV.js} +2 -2
- package/dist/ui/assets/{quick-llm-BENSRBxD.js → quick-llm-ByDw79ph.js} +1 -1
- package/dist/ui/assets/realm-inprocess-BLSq-cQS.js +33 -0
- package/dist/ui/assets/{recovery-screen-Tq3wGnmV.js → recovery-screen-CldB31e2.js} +1 -1
- package/dist/ui/assets/{redirect-uri-Dnc7Ae7l.js → redirect-uri-DlOE4xJ4.js} +1 -1
- package/dist/ui/assets/{remote-terminal-view-C8LiE7Sl.js → remote-terminal-view-BpHAWWeD.js} +1 -1
- package/dist/ui/assets/{require-guards-DPd9EYEo.js → require-guards-DPf5QETL.js} +14 -3
- package/dist/ui/assets/{rest-ops-vPHUnWou.js → rest-ops-CPEMTyP-.js} +1 -1
- package/dist/ui/assets/{rest-ops-DABw6D2a.js → rest-ops-DKsDUOxE.js} +1 -1
- package/dist/ui/assets/{restore-DVP0SPlY.js → restore-CaWVAwJN.js} +1 -1
- package/dist/ui/assets/{run-CPT6_R-_.js → run-2oVsdzZj.js} +2 -2
- package/dist/ui/assets/{run-BGZP3j5p.js → run-BA94mtBB.js} +1 -1
- package/dist/ui/assets/{run-CKp8YCSO.js → run-BOMvKGOX.js} +1 -1
- package/dist/ui/assets/{run-C-OJvA2b.js → run-BP0Pyqp1.js} +1 -1
- package/dist/ui/assets/{run-BKItPKix.js → run-BVlVC7jS.js} +10 -10
- package/dist/ui/assets/{run-CX6BUmN-.js → run-C1e4W7pJ.js} +1 -1
- package/dist/ui/assets/{run-CiUSMP3Y.js → run-CH8M889A.js} +3 -3
- package/dist/ui/assets/{run-zLr2k5ZP.js → run-CHKo3g64.js} +1 -1
- package/dist/ui/assets/{run-BV_OY-0v.js → run-CW0-cog1.js} +1 -1
- package/dist/ui/assets/{run-FyOp8GaC.js → run-Dl4OQ_Yd.js} +1 -1
- package/dist/ui/assets/{screencapture-run-BnyfqO9F.js → screencapture-run-Dt7lEfIp.js} +1 -1
- package/dist/ui/assets/{secret-env-Cazzr31P.js → secret-env-iwcdxjpw.js} +1 -1
- package/dist/ui/assets/secret-topology-CZv6A9HW.js +1 -0
- package/dist/ui/assets/{secrets-bridge-client-CiA-2ZIU.js → secrets-bridge-client-B_CG-HIg.js} +1 -1
- package/dist/ui/assets/{secrets-bridge-client-kWWipUuL.js → secrets-bridge-client-CUClOhLJ.js} +1 -1
- package/dist/ui/assets/{session-command-memory-HovmIuMP.js → session-command-memory-mbpebhz_.js} +2 -2
- package/dist/ui/assets/{session-jsonl-PCOch0_B.js → session-jsonl-BIxfXb7u.js} +1 -1
- package/dist/ui/assets/{session-jsonl-DK-0Hm8U.js → session-jsonl-v8KN4g_6.js} +1 -1
- package/dist/ui/assets/{session-search-index-BNvhiRLv.js → session-search-index-CDR-vSNN.js} +1 -1
- package/dist/ui/assets/{setup-feature-flags-remote-2RQqEGaT.js → setup-feature-flags-remote-zauTrEL6.js} +1 -1
- package/dist/ui/assets/{setup-model-catalog-B911rONc.js → setup-model-catalog-CC8KX0LB.js} +1 -1
- package/dist/ui/assets/shared-BDXQKitm.js +1 -0
- package/dist/ui/assets/shared-DfMXB1pm.js +4 -0
- package/dist/ui/assets/{signed-fetch-gB25Bw53.js → signed-fetch-D3crNsSs.js} +1 -1
- package/dist/ui/assets/{silent-renew-backoff-Brcr76an.js → silent-renew-backoff-Bw_c5Dy9.js} +1 -1
- package/dist/ui/assets/slicc-version-BiZFSvt-.js +1 -0
- package/dist/ui/assets/{snapshot-Ci8jnY_A.js → snapshot-CISX8Ybx.js} +1 -1
- package/dist/ui/assets/{snapshot-features-BYEGCVfZ.js → snapshot-features-NmqWLNc6.js} +1 -1
- package/dist/ui/assets/soundscape-Cmr7xPXZ.js +1 -0
- package/dist/ui/assets/{speak-BT4Xdlvj.js → speak-D1JF7Loc.js} +1 -1
- package/dist/ui/assets/{sprinkle-manager-8T1XJmL3.js → sprinkle-manager-DYhvCg3m.js} +1 -1
- package/dist/ui/assets/{sprinkle-renderer-_vOdPt8H.js → sprinkle-renderer-B5O28doL.js} +16 -16
- package/dist/ui/assets/{ssh-BGtVaOJn.js → ssh-DPyBpV4Z.js} +1 -1
- package/dist/ui/assets/{state-nnDajRje.js → state-CcrU6NJk.js} +1 -1
- package/dist/ui/assets/{store-D9XL9aIQ.js → store-D9HgQRdy.js} +1 -1
- package/dist/ui/assets/{store-BxxMrsS7.js → store-DYTzgtEI.js} +1 -1
- package/dist/ui/assets/{sudo-CnXURhFV.js → sudo-BMKHhJ-G.js} +1 -1
- package/dist/ui/assets/{supervisor-fOZGoD0G.js → supervisor-DVWawJTM.js} +1 -1
- package/dist/ui/assets/{sync-dialog-oh_O9Mv5.js → sync-dialog-a3YQ7KdL.js} +1 -1
- package/dist/ui/assets/{transformers-env-zmBF2In6.js → transformers-env-BFCBqwlr.js} +1 -1
- package/dist/ui/assets/{tray-leader-Co-1rs4b.js → tray-leader-zKkaFxIk.js} +1 -1
- package/dist/ui/assets/{tray-leave-runtime-PpRt0K76.js → tray-leave-runtime-CNc8g3tP.js} +1 -1
- package/dist/ui/assets/{tray-runtime-config-DBapPARd.js → tray-runtime-config-DmcF9Q2r.js} +1 -1
- package/dist/ui/assets/{tray-sidecar-DYuM8Ifi.js → tray-sidecar-DqV3sh8H.js} +1 -1
- package/dist/ui/assets/{turn-journal-_Hrn3Aoy.js → turn-journal-Z3e-huge.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-jhAzOLSL.js → upgrade-detection-Bsvrq_lt.js} +1 -1
- package/dist/ui/assets/{upskill-lick-skip-D1vFOKgN.js → upskill-lick-skip-BQvJXwS8.js} +1 -1
- package/dist/ui/assets/voice-reply-B_BXV7A4.js +1 -0
- package/dist/ui/assets/{wc-attach-BadBFuc7.js → wc-attach-CEkWqpLf.js} +2 -2
- package/dist/ui/assets/{wc-browser-DqUJaWcH.js → wc-browser-DWwB9l3P.js} +1 -1
- package/dist/ui/assets/{wc-computers-CTPf-95b.js → wc-computers-DuGnds2T.js} +1 -1
- package/dist/ui/assets/{wc-detached-UIVnUZp5.js → wc-detached-Bc462J4O.js} +1 -1
- package/dist/ui/assets/{wc-extension-Bxc7adS3.js → wc-extension-DZmTjBxS.js} +2 -2
- package/dist/ui/assets/{wc-follower-CcQiy0Ep.js → wc-follower-C0olroj0.js} +3 -3
- package/dist/ui/assets/{wc-follower-oauth-DEBzlhDR.js → wc-follower-oauth-BTOd3V8J.js} +1 -1
- package/dist/ui/assets/{wc-gelatiere-B3T_MsYw.js → wc-gelatiere-3BBfbusI.js} +1 -1
- package/dist/ui/assets/wc-live-Cc8h6ZH1.js +428 -0
- package/dist/ui/assets/{wc-nav-Cc31eRBb.js → wc-nav-6f53F2ez.js} +2 -2
- package/dist/ui/assets/{wc-onboarding-A_43Ieo4.js → wc-onboarding-D548eykf.js} +2 -2
- package/dist/ui/assets/{wc-permissions-C9woviuI.js → wc-permissions-B3SJSMf9.js} +2 -2
- package/dist/ui/assets/{wc-placeholder-t7YMeS_j.js → wc-placeholder-BS4TqwFw.js} +3 -3
- package/dist/ui/assets/{wc-secret-request-Dfm9QvyE.js → wc-secret-request-DoSFP6An.js} +1 -1
- package/dist/ui/assets/{wc-settings-BD6JONjg.js → wc-settings-B7zv9hl_.js} +3 -3
- package/dist/ui/assets/{wc-shell-Ba5uNdmF.js → wc-shell-pTQjFXvS.js} +362 -199
- package/dist/ui/assets/{wc-shortcut-config-DeU2Q2BU.js → wc-shortcut-config-87PqTsLw.js} +2 -2
- package/dist/ui/assets/{wc-sprinkles-D4sQVRG5.js → wc-sprinkles-BMoFsMix.js} +2 -2
- package/dist/ui/assets/{wc-transcript-export-CsUhlwMo.js → wc-transcript-export-CP5fUab6.js} +2 -2
- package/dist/ui/assets/{wc-tray-CwvVidS8.js → wc-tray-DW-miPAR.js} +4 -4
- package/dist/ui/assets/welcome-detection-F6VKt_Ix.js +1 -0
- package/dist/ui/assets/{whisper-session-MB0YiZ0q.js → whisper-session-C90nWMGE.js} +2 -2
- package/dist/ui/assets/{xai-grok-rwJAZiuv.js → xai-grok-D0yrS3by.js} +1 -1
- package/dist/ui/assets/{xai-grok-0BxCS34q.js → xai-grok-U-6wly9W.js} +1 -1
- package/dist/ui/index.html +9 -9
- package/dist/ui/packages/webapp/index.html +9 -9
- package/package.json +1 -1
- package/dist/ui/assets/SKILL-CWxUIsrD.js +0 -1
- package/dist/ui/assets/__vite-browser-external-BlQ4QU88.js +0 -1
- package/dist/ui/assets/js-realm-worker-BIVxi3Fa.js +0 -50
- package/dist/ui/assets/jsh-executor-DhdH2_yG.js +0 -18
- package/dist/ui/assets/provider-store-access-BFeKOGcv.js +0 -1
- package/dist/ui/assets/provider-store-access-C2X0AryS.js +0 -1
- package/dist/ui/assets/proxied-fetch-BlIpeDaL.js +0 -2
- package/dist/ui/assets/proxied-fetch-CNe6icTW.js +0 -1
- package/dist/ui/assets/py-realm-worker-DLb3nkyD.js +0 -244
- package/dist/ui/assets/realm-inprocess-BBw2Fa8u.js +0 -30
- package/dist/ui/assets/secret-topology-DpJD8DzV.js +0 -1
- package/dist/ui/assets/shared-C_68vn79.js +0 -4
- package/dist/ui/assets/shared-mBEktoxE.js +0 -1
- package/dist/ui/assets/slicc-version-CAje2bW-.js +0 -1
- package/dist/ui/assets/soundscape-CdQvLeB0.js +0 -1
- package/dist/ui/assets/voice-reply--Z1dXvPT.js +0 -1
- package/dist/ui/assets/wc-live-B4gJYGS5.js +0 -428
- package/dist/ui/assets/welcome-detection-CIDbn0ev.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e='---\nname: delegation\ndescription: |\n Use this when deciding whether to do work yourself or delegate to a scoop, when\n fanning out parallel scoops, or when picking models for sub-agents. Covers\n scoop lifecycle (when to drop), parallel orchestration (`scoop_mute`,\n `scoop_unmute`, `scoop_wait`), one-shot ephemeral sub-agents via the `agent`\n shell command, and model selection. Read this BEFORE running `scoop_scoop` for\n non-trivial work.\nallowed-tools: bash\n---\n\n# Delegation\n\nScoops do the heavy lifting; the cone orchestrates and synthesizes. This skill is about choosing how to delegate, not about administering scoops.\n\n## When to delegate\n\n**Default to delegation.** Parallel scoops almost always finish faster, and the cone\'s job is synthesis.\n\nDelegate when:\n\n- Multiple independent sources (scraping 3 sites = 3 scoops).\n- Time-consuming work that doesn\'t need direct oversight.\n- Work expressible as a clear, self-contained brief.\n\nDo it yourself when:\n\n- Single quick lookup (one page, one API call).\n- Real-time adaptation needed (navigating broken URLs).\n- Overhead of spawning exceeds benefit.\n\n## Brief for authority, not for execution\n\nThe most common delegation failure is **the cone doing too much pre-work before delegating**. The cone researches the topic, makes the design decisions, picks the approach, and then hands the scoop a pre-cooked plan to type out. This is bad on three axes:\n\n- **Pollutes the scoop\'s context.** The brief is bloated with conclusions the scoop now has to re-derive an opinion on, instead of facts it can act on.\n- **Strips the scoop of autonomy.** A scoop that\'s been told what to think can\'t push back on a bad call or notice a better path mid-task. You get a typist, not a collaborator.\n- **Wastes the cone\'s tokens.** The cone\'s strength is orchestration — picking the right scoops, synthesizing their outputs. Doing the research itself burns the cone\'s context on work that\'s parallelizable.\n\n**Heuristic: if the cone reads files, runs commands, or makes decisions before delegating, that should have been part of the scoop\'s brief.** Hand the scoop the question, the constraints, and the access — let it decide.\n\n| Bad (cone over-prepares) | Good (scoop decides) |\n| ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |\n| Cone reads 5 files, picks an approach, tells scoop "implement approach X in file Y." | Scoop is told "the user wants Z; the relevant code is under `/workspace/src/`. Pick an approach and implement it." |\n| Cone scrapes 3 docs, summarizes, then asks scoop to "write a comparison based on this summary." | Scoop is told "compare libraries A, B, C for this use case. Their docs are at <urls>. Decide and write the comparison." |\n| Cone debugs a failure, isolates the bug, then asks scoop to "fix the off-by-one in line 42." | Scoop is told "this command fails with `<output>`. Find and fix the bug." (Even better: a one-shot `agent` for cheap, deterministic bug-fixes.) |\n\nTwo carve-outs where pre-work IS the cone\'s job:\n\n1. **Routing decisions.** Picking which scoop gets which slice of a fan-out is orchestration, not pre-cooking.\n2. **Synthesis after the fact.** Reading scoop outputs to combine them is the cone\'s whole point.\n\nIf a scoop comes back with a wrong call, **drop it and re-spawn with a better brief** — don\'t feed a correction. Corrections compound the autonomy problem.\n\n## Scoop lifecycle\n\nDrop scoops when done — but **NEVER drop a scoop that owns a sprinkle**.\n\nDrop when:\n\n- Task completed and results synthesized.\n- Stuck or misbehaving (drop and re-spawn with a better brief).\n\nNever drop when:\n\n- It owns an open sprinkle (must stay alive for the sprinkle\'s lifetime).\n- Running a recurring/long-running task (feed watcher, webhook handler).\n- Work is still in progress (dropping loses all context).\n\n## Three delegation primitives\n\n| Primitive | Conversation | Sprinkle ownership | Auto-cleanup | When to use |\n| ---------------------------- | ------------------------ | ------------------ | ----------------- | ----------------------------------------------------------- |\n| `scoop_scoop` + `feed_scoop` | Persistent (multi-turn) | Yes | No (`drop_scoop`) | Long-lived work; sprinkle owners; conversational follow-ups |\n| `scoop_wait` / `scoop_mute` | Persistent (multi-turn) | Yes | No | Parallel fan-out where you want one synthesis turn |\n| `agent` (shell command) | One-shot (single prompt) | No | Yes (on exit) | Cheap, predictable, composable; no cone follow-up needed |\n\n### `agent` — one-shot ephemeral sub-agents\n\n`agent` is a shell command that spawns a one-shot sub-scoop, feeds it a prompt, blocks until the agent loop completes, and prints the final message on stdout. Runs from any bash context (terminal, `feed_scoop` prompt, `.jsh` script, dip lick handler, sprinkle button handler).\n\n```\nagent <cwd> <allowed-commands> <prompt> [--model <id>] [--workspace-mode <mode>] [--read-only <paths>] [--background-after <s>]\n```\n\n- `<cwd>` — sole writable prefix (plus `/shared/`, the scoop\'s scratch folder, and `/tmp/` — the whole shared scratch tree, so a scoop can always reach its `$TMPDIR`). Relative paths resolve against the caller\'s cwd.\n- `<allowed-commands>` — comma-separated allow-list; `*` for unrestricted.\n- `<prompt>` — forwarded verbatim. The spawned scoop has no access to the caller\'s history; pack context into the prompt.\n- `--model` — defaults to the parent scoop\'s model (or the cone\'s, when invoked from the terminal). Accepts an exact id, a shorthand (`haiku`, `sonnet`, `claude-haiku-4-5`), or the `provider:model` form `models` prints (`openrouter:openai/gpt-5.6-terra-pro`). A bare id resolves against the selected provider first, then against any other configured provider that offers it; if several do, the error lists the qualified ids to pick from. The scoop runs on the provider the model resolved from. An id that cannot be resolved is a hard error (exit 1) — it never silently falls back to the parent\'s model. Models from a provider other than the selected one must additionally be allowed in `/etc/models` (see [Model access policy](#model-access-policy)); a blocked model exits 1 and the error quotes the line to add.\n- `--workspace-mode` — isolation policy. Default `shared-readonly` is today\'s sandbox (parent workspace visible, cwd + `/shared/` + scratch writable, mounts readable). `private` is an isolated sandbox: own cwd/scratch only — no parent workspace, no implicit `/shared/`, mounts are **not** auto-visible. `snapshot` and `shared-live` are not implemented and exit 1.\n- `--read-only` — pure-replace list of read-only paths. Default: the **owning cone\'s** workspace (plus `/workspace/skills/`) and the invoking shell\'s cwd. When replacing it, name your own cone\'s workspace — a literal `/workspace/` is the primary cone\'s, not yours. Under `--workspace-mode private` the default visible list is empty.\n- `--background-after <seconds>` — how long the spawned scoop\'s `bash` waits for a command before detaching it and moving on (default 600). Nobody can cancel a spawned scoop\'s turn, so a command that never returns would otherwise burn the whole run on one call; a detached command reports its exit code back to that scoop as a `Background Command` lick. `0` detaches every command immediately.\n\n**Critical property: no handoff.** Ephemeral scoops do NOT notify the cone on completion. Running `agent` from a non-cone shell does not trigger an unsolicited cone turn. The caller gets the result on stdout, nothing else. This makes `agent` the right choice for cheap, predictable interactions inside dips and sprinkles where you don\'t want the cone or owning scoop woken up.\n\n```bash\n# Parallel, collected to one file. No cone turns spawned.\nfor url in site-a site-b site-c; do\n agent "$TMPDIR" "curl,jq" "Fetch https://$url/api, return the top-level title field." >> "$TMPDIR/titles.txt" &\ndone\nwait\n\n# Focused refactor with a restricted allow-list.\nagent /workspace/src "rg,sed,node" "Rename getCwd to getCurrentWorkingDirectory across *.ts"\n\n# Cheap one-shot from a faster model.\nagent . \'*\' \'Summarize the README in one sentence.\' --model claude-haiku-4-5\n```\n\nFor dip and sprinkle interaction patterns built on `agent`, see `/workspace/skills/dips/SKILL.md` and `/workspace/skills/sprinkles/SKILL.md`.\n\n## Shaping the scoop\'s sandbox: `workspaceMode`, `visiblePaths`, `writablePaths`, `allowedCommands`\n\n`scoop_scoop` accepts a named isolation mode plus three sandbox-shaping parameters. **They are how you give a scoop authority — by widening or narrowing what it can read, write, and run.** A scoop that can\'t reach the files it needs has no autonomy; pre-cooking the brief is what you do to compensate. Get the sandbox right and the brief gets shorter.\n\nA rarely used argument — `canCreateChildren: true` — is an explicit nested-delegation grant. The new scoop may then `scoop_scoop` / `feed_scoop` / `drop_scoop` its own children. Omit it (the default) so the scoop stays a leaf. A scoop that was not itself granted this cannot pass it on.\n\n| Param | What it controls | Default | Pure replace? |\n| ------------------- | --------------------------------------- | -------------------------------------------------------------------------------------- | ------------- |\n| `workspaceMode` | Sharing policy for the parent workspace | `"shared-readonly"` (today\'s scoop). `"private"` isolates. | Yes |\n| `visiblePaths` | Read-only VFS paths the scoop can SEE | shared-readonly: `["/workspace/"]`; private: `[]` | Yes |\n| `writablePaths` | VFS paths the scoop can READ AND WRITE | shared-readonly: `["/scoops/<folder>/", "/shared/"]`; private: `["/scoops/<folder>/"]` | Yes |\n| `allowedCommands` | Shell command allow-list | unrestricted (every built-in + `.jsh`) | Yes |\n| `canCreateChildren` | Nested-delegation grant | `false` (leaf scoop) | Yes |\n\n"Pure replace" means what you set is what you get — the value isn\'t merged with the default. Pass `[]` to drop it entirely. Trailing slashes recommended on path entries (e.g. `/shared/data/`).\n\nSubtleties:\n\n- **`writablePaths` are always readable too.** A true read-nothing sandbox needs both `visiblePaths: []` AND `writablePaths: []`.\n- **Mounts remain readable in `shared-readonly`** (the default) regardless of `visiblePaths`. `private` does **not** auto-include mounts — name a mount in `visiblePaths` / `writablePaths` if the scoop needs it. That\'s how a mount cannot silently expand every child\'s authority.\n- **`allowedCommands` applies recursively** — pipelines, command substitutions, and network commands are all gated. Pass `["*"]` for explicit unrestricted.\n- **List every absolute root the scoop\'s tools write to.** Include any root outside the scoop\'s folder and `/tmp/` in `writablePaths` (any spelling works: `/x`, `/x/`, `/x/**`), or every such write escalates to you for approval. `playwright-cli` needs nothing here: in a scoop its session log, snapshots and screenshot archive go to the scoop\'s own `$TMPDIR/.playwright/`, not `/.playwright/`. Prefer pointing skills at `/scoops/<folder>/`, `/shared/`, or `/tmp/` over inventing new VFS roots.\n\n### How to choose\n\nAsk three questions, in order:\n\n1. **What does the scoop need to read?** Files it must consume — source tree, docs, mounted data — go in `visiblePaths`. Default `/workspace/` is fine for most "do something in the project" briefs. Add specific paths (`/shared/data/`, `/mnt/da/`) for narrower scopes.\n2. **What is it allowed to change?** Output dirs, the file it\'s editing, the sprinkle path it owns. Default writable scratch (`/scoops/<folder>/`) plus `/shared/` is fine for scoops producing artifacts. **Tighten this when the scoop should not modify production code** — e.g. a research scoop with `writablePaths: ["/scoops/<folder>/"]` only.\n3. **What commands does it need?** Default unrestricted is right when the scoop will figure out its own toolchain. Tighten only when narrowing to a known set of tools improves reliability or safety — e.g. a scraper with `["curl","jq"]`, or a refactor scoop with `["rg","sed","node"]`.\n\n### Patterns\n\n```\n# Default sandbox (shared-readonly): full project, own scratch dir, all commands.\n# Right for most "do this in the project" briefs.\nscoop_scoop({ name: "fix-bug", prompt: "..." })\n\n# Isolated sandbox. Cannot see the parent workspace or mounts.\nscoop_scoop({ name: "scratch-only", workspaceMode: "private", prompt: "..." })\n\n# Read-only research scoop. Can read everything, can\'t change anything,\n# can use whatever commands it needs to investigate.\nscoop_scoop({\n name: "auth-research",\n visiblePaths: ["/workspace/", "/shared/"],\n writablePaths: ["/scoops/auth-research/"],\n prompt: "Map the auth flow across the codebase and write findings to /scoops/auth-research/notes.md."\n})\n\n# Sprinkle-owning scoop. Needs write access to its sprinkle path.\nscoop_scoop({\n name: "giro-winners",\n writablePaths: ["/scoops/giro-winners/", "/shared/sprinkles/giro-winners/"],\n prompt: "..."\n})\n\n# Tight scraper. Only sees public docs, only runs network/json tools,\n# writes to its own scratch.\nscoop_scoop({\n name: "scraper",\n visiblePaths: [],\n writablePaths: ["/scoops/scraper/"],\n allowedCommands: ["curl", "jq", "rg"],\n prompt: "Fetch <urls>, extract the price field from each, write a CSV to /scoops/scraper/out.csv."\n})\n\n# Mount-only scoop. Sees just the DA mount, can write back to it.\n# Under shared-readonly, mounts stay readable even with visiblePaths: [].\n# Under private, name the mount in writablePaths (or visiblePaths) or it is hidden.\nscoop_scoop({\n name: "da-editor",\n writablePaths: ["/mnt/da/", "/scoops/da-editor/"],\n prompt: "Edit /mnt/da/index.html to ..."\n})\n```\n\n### Don\'t\n\n- Don\'t widen `writablePaths` "just in case." A scoop with surprise write access can clobber files outside its task — drop and re-spawn with a tighter scope is cheaper than recovering from that.\n- Don\'t narrow `allowedCommands` to "look secure" if you don\'t know which commands the scoop will need. The agent will hit a wall mid-task and either lie ("I\'ll skip that step") or spam tool errors.\n- Don\'t forget the `writablePaths`-also-readable rule. `visiblePaths: []` alone does not produce a blind sandbox.\n\n### `background_after` — bounding a scoop\'s slow commands\n\n`scoop_scoop({ background_after: <seconds> })` sets how long that scoop\'s `bash`\ntool waits for a command before detaching it and continuing the turn (default\n600). This is a resilience knob, not a sandbox one: a scoop\'s turn cannot be\ncancelled by anyone, so a command that never returns would hang the scoop (and,\nfor a blocking `agent` call, its caller) indefinitely. When a detached command\nfinishes, that scoop receives a `Background Command` lick with the exit code, a\npreview, and the path to the full output.\n\n```\n# A scoop whose commands must never stall it for more than a minute.\nscoop_scoop({ name: "flaky-net", background_after: 60, allowedCommands: ["curl", "jq"], prompt: "..." })\n```\n\nThe scoop can also override the budget per call (`bash({ command, background_after, timeout })`). Each run is a real pid: `ps` lists a detached job and `kill <pid>` stops it, so `timeout` is a kill rather than just a detach.\n\n### Filesystem work limits\n\nScoop shell commands that use the upstream limits stop at 100,000 traversal entries,\n256 levels, 32 MiB input, or 64 MiB of tracked live buffers. Split large trees or\ninputs across calls when a command reports a limit. Environment variables cannot\nraise these limits. They are not an OPFS quota and do not cover custom `tar`,\n`unzip`, JavaScript, Python, Git, or browser command implementations. Existing\n`timeout` and `background_after` controls still apply independently.\n\n## Parallel orchestration: `scoop_mute` / `scoop_unmute` / `scoop_wait`\n\nBy default, every non-ephemeral scoop completion fires a `scoop-notify` event that wakes the cone for a fresh turn. Fanning out N scoops in parallel produces N extra cone turns whose only job is to acknowledge "scoop X finished" — expensive in tokens, disruptive to orchestration.\n\nThese three cone-only tools collapse the fan-out into a single follow-up turn:\n\n- **`scoop_mute({ scoop_names })`** — suspends `scoop-notify` for the listed scoops. Completions arriving while muted are stashed (full response persisted to `/shared/scoop-notifications/*.md`); they do NOT trigger a cone turn.\n- **`scoop_unmute({ scoop_names })`** — resumes notifications AND returns every stashed completion inline as the tool\'s result. The cone reads the summaries in the current turn instead of one extra turn per scoop.\n- **`scoop_wait({ scoop_names, timeout_ms? })`** — schedules a NON-BLOCKING wait. Returns immediately so the cone can keep working; when every listed scoop completes (or the timeout fires) the orchestrator delivers a single `scoop-wait` channel lick with all captured summaries. Target scoops are implicitly muted for the wait\'s duration so individual `scoop-notify` events don\'t pre-empt the eventual lick. `timeout_ms: 0` fires on the next tick with whatever is already done. Omit `timeout_ms` to wait indefinitely. Pre-existing `scoop_mute` state survives the wait.\n\n### When to use which\n\n> Every scoop name you pass to `feed_scoop`, `drop_scoop`, `scoop_mute`, `scoop_unmute` or `scoop_wait` is resolved against what `list_scoops` shows you. Another cone\'s identically named scoop is never matched by a bare name; the call errors instead (#2360).\n>\n> If you are the LEADING cone, `list_scoops` also shows scoops you do not own, tagged `[INHERITED]` (their owning cone is gone) or `[FOREIGN: <cone>]` (another cone owns them). You can feed, wait on, mute and drop those — but only by passing `cross_cone: true` in the same call. Omitting it is an error, not a no-op, so you never touch another cone\'s work by accident. Two things to know before you do: a cross-cone `feed_scoop` sends its completion notice to the scoop\'s OWN owner, so use `scoop_wait ({ …, cross_cone: true })` if you need the result yourself; and a batch call refuses the whole batch when any listed scoop needs the flag.\n\n- **Fan-out + synthesis** (your next useful step depends on all scoops) → `scoop_wait`.\n- **Background work you\'ll check later** → `scoop_mute` now, `scoop_unmute` when you want the summaries.\n- **Single delegation, no parallelism** → don\'t mute. Default `scoop-notify` is fine.\n\n### Examples\n\n```\n# Fan-out + synthesize:\nfeed_scoop({ scoop_name: "writer-a", prompt: "Draft intro" })\nfeed_scoop({ scoop_name: "writer-b", prompt: "Draft outro" })\nscoop_wait({ scoop_names: ["writer-a", "writer-b"], timeout_ms: 600000 })\n# Returns immediately. Cone keeps working. When both finish (or 10 min), a single\n# `scoop-wait` lick wakes the cone with both summaries; the next turn synthesizes.\n\n# Background, poll non-blockingly:\nscoop_mute({ scoop_names: ["scraper"] })\nfeed_scoop({ scoop_name: "scraper", prompt: "Collect URLs from the sitemap" })\n# ... do other work ...\nscoop_unmute({ scoop_names: ["scraper"] })\n# Tool result has the stashed summary or "No stashed completions".\n```\n\n### Liveness: a quiet fan-out is not a finished fan-out\n\n"Files look stable and the scoops are still processing" does NOT mean the work\nis done or progressing. A common stall mode: one or more scoops are blocked on\na pending approval (`sudo_request`) that never reached you, and the whole\nfan-out sits idle while you wait for a `scoop-wait` lick that can\'t arrive.\n\nWhen a fan-out goes quiet — no completions, no file changes, no progress\nmessages — run `list_sudo_requests` before concluding anything. Resolve or\ndeny each pending request, then keep waiting. Only treat a fan-out as\ncomplete when every scoop has actually reported completion (via `scoop_wait`\nlick or `scoop_unmute` summaries), not because output files stopped changing.\n\nWhen you **deny** one, pass `reason` to `lick_dismiss`. The scoop sees the\nsubject of its own request and your verdict; without a reason it cannot tell a\nrefusal from a misunderstanding, so it retries the same thing or invents a\nworkaround. Say what would make it acceptable, if anything would.\n\n### Notes\n\n- Full response is always persisted to `/shared/scoop-notifications/<timestamp>-<folder>-<id>.md` (bounded to the 200 most recent). The summary string in the tool result is truncated at 20 000 characters; read the VFS path when you need the full output.\n- Unknown scoop names are reported in the result but do not abort the call.\n- Dropping or re-registering a muted scoop is safe: `unregisterScoop` releases waiters (resolving as `timedOut: true`) and clears mute/pending state.\n\n## Inspecting delegation cost\n\nRun `cost` to inspect spend for the cone and currently live scoops. The table labels each row\'s source, and `--json` returns the same live-only scope for scripts as `{"budget": <window|null>, "scoops": [ ... ]}` — read the rows from `.scoops`. The Model column (JSON `model`) is the model that unit is running now — its provider-qualified pin, the id half of `models`\' "Currently using" line (a provider-less legacy pin yields to the latest turn when they differ) — not whichever model took the most turns. JSON `models` lists each distinct model once: a bare id and a region- or version-qualified id of the same model are one entry (a suffixed variant such as `-fast` is not), and the entry for the model in use now keeps that current spelling.\n\nOn a provider billing against a rolling allowance (rather than per token), `cost` leads with that budget: percent USED of the window and when it resets. That number, not the dollar total, is what says whether long runs will finish — the allowance is shared, so it can be most of the way gone before this session has cost a cent.\n\nUse `cost --all` only when you need the historical picture: it adds dropped scoops from the current runtime and frozen sessions recorded in `/sessions/index.json`. Combine it with `--json` for structured historical output. Legacy frozen sessions that predate cost persistence are retained with unknown cost rather than reported as zero.\n\n## Model selection for scoops\n\n**Always run `models` to verify available models before specifying one.** Model availability depends on the configured provider and API key. `model` accepts an exact id, a shorthand (`haiku`, `sonnet`, `claude-haiku-4-5`), or the `provider:model` form `models` prints (`openrouter:openai/gpt-5.6-terra-pro`). A bare id resolves against the selected provider first, then against any other configured provider that offers it; matching several is an error listing the qualified ids. The scoop runs on the provider the model resolved from. An id that cannot be resolved is rejected outright — it never silently falls back to the cone\'s model. Cross-provider models also need an `/etc/models` allow entry (see [Model access policy](#model-access-policy)).\n\nUse `models --json` to compare. Intelligence, speed, and cost are independent dimensions:\n\n- **Cost-sensitive** (renames, formatting, grep-and-replace) → low-cost models.\n- **Complex** (architecture, multi-file refactors, subtle debugging) → high-intelligence.\n- **Latency-sensitive** (interactive workflows, quick lookups) → high-speed.\n- **Default** → omit the parameter; inherits the cone\'s model.\n\n```bash\n# 1. Verify available models.\nmodels\n\n# 2. Create scoops with verified IDs.\nscoop_scoop({ name: "fix-typos", model: "claude-haiku-4-5", prompt: "Fix all typos in /workspace/docs/" })\nscoop_scoop({ name: "architect", model: "claude-opus-4-6", prompt: "Design the new plugin system..." })\n```\n\n**Error handling**: scoops retry up to 3 times with exponential backoff for transient errors (rate limits, server errors). Non-retryable errors (invalid model, auth failures) fail immediately and notify the cone, bypassing any `scoop_mute` settings.\n\n## Browser tabs\n\nBrowser-tab handling rules (track your IDs, never close tabs you didn\'t open, handle "tab not found" gracefully) live in `/workspace/skills/playwright-cli/SKILL.md` under "Multi-Agent Tab Behavior". Read that skill before delegating browser work.\n\n### Browser-driving scoops: one tab per scoop\n\nAll scoops share ONE browser, but `playwright-cli` locks **per tab**: commands\non different tabs run in parallel, and commands on the same tab serialize. One\nscoop\'s slow or hung navigation stalls only its own tab. Fan-out across tabs\ngenuinely multiplies browser throughput.\n\n- **Give every browser-driving scoop its own tab; no fan-out cap is needed\n for correctness.** Browser scoops fan out like any other work, and commands\n on distinct tabs really do run side by side.\n- **Two scoops on the SAME tab still queue.** If a slice needs a shared tab,\n hand it to one scoop rather than splitting it.\n- A hung navigation stalls only its own tab, so one stuck scoop no longer\n detaches every sibling\'s commands past `background_after`.\n- When a command emits `note: browser bridge contended — ...` on stderr (total\n lock wait + queue depth, and whether the wait was on **this tab** or\n **bridge-wide**), that is back-off guidance: instruct the scoop to stagger or\n move to its own tab, never to re-run the command — it already ran; it was\n just slow to get the lock. A bridge-wide wait points at the few genuinely\n global operations (`tab-select`, `--foreground`, attaching to a tab), not at\n your tab.\n- Scoops doing CPU/VFS/network work (curl, file edits, analysis) fan out\n freely, as before.\n\n## Model access policy\n\n`/etc/models` decides which `provider:model` combinations you may spawn against. It\nis keyed by the **selected** provider:\n\n```ini\n[adobe] # while `adobe` is selected…\nopenrouter:* # …any openrouter model may be targeted\n-adobe:claude-opus-5 # …but never adobe\'s own Opus 5\n```\n\n- The selected provider\'s own models are allowed by default — you never list them.\n- Any other provider\'s model is denied until an entry allows it. This is a spend\n boundary: the user may keep separate work and personal accounts.\n- `models` and the picker hide only what a `-` entry denies, so a model you can see\n is not automatically one you may spawn against; the rejection names the exact\n line to add.\n\nDo not edit `/etc/models` to unblock yourself — the write requires a human approval\n(`Write /etc/models` in `/etc/sudoers`). Ask the user instead, quoting the line.\n';export{e as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e="---\nname: computer\ndescription: |\n Use this when looking at and poking a screen with SLICC's `computer`\n shell command (xdotool grammar). Covers v86 guests (`v86:<name>`),\n browser tabs (`tab:<id>`), display share (`screen:<handle>`), follower\n desktops (`ssh:<runtimeId>`), HTTP remotes (`url:<host>`), jsh-hosted\n backends, screenshot-space coordinates, frozen JPEG frames, and chaining\n click/type/key.\nallowed-tools: bash\n---\n\n# computer — look at and poke a screen\n\n`computer` is the look-then-act loop for every registered screen. Prefer it over `v86 type|key|mouse|screenshot|text` once a guest is running. `v86 start` still boots the VM and registers `v86:<name>`.\n\n## Target\n\n```bash\ncomputer ls # ids, kinds, names, titles\ncomputer use v86:arch # default for later verbs\ncomputer info # descriptor for the current target\n```\n\nResolution order: `-c` / `--computer`, else `$COMPUTER`, else last `computer use`, else the only registered computer.\n\n`-c` takes an id, the `-n` name the computer was added under, or its current title — in that order. Prefer the **id** or the **name**: `ls` shows them in separate columns because `TITLE` is whatever the surface calls itself right now (a tab republishes `document.title` on every capture), while `NAME` is fixed for the life of the registration.\n\n## Look then act\n\nEvery poke writes a frozen JPEG and prints `target: <id>` then `screen: <path>`. The extension of that path follows the bytes, so it is `.jpg` in practice and `.png` only if a backend hands back PNG the runtime could not transcode — read the extension, do not assume it. The `target:` line is the resolved computer (`-c`, `$COMPUTER`, last `use`, or the only registered one) so a later `computer screenshot` without `-c` still binds the bash-row UI. That poke frame is a transcript side effect only — it does **not** redefine the coordinate space, so never read new coordinates off it (it can come back narrower and your clicks would map to the wrong place). Run a fresh `computer screenshot` before choosing coordinates for the next click. If the frozen frame cannot be captured, the poke still exits 0 (it already happened); do not retry the same click.\n\n```bash\ncomputer screenshot # JPEG; prints WxH → wxh (scale s)\ncomputer screenshot --size high out.jpg\ncomputer text # text-mode dump when the backend supports it\n```\n\n`--size` is `low` (256), `medium` (768, default), `high` (1536), or a max width. It is an upper bound: a frame is never widened past the computer's native size to reach it, and one that cannot be encoded that narrow arrives at native size rather than upscaled. Coordinates on later verbs are in that last `computer screenshot` (the last model-facing shot) unless `--native` — a poke's frozen frame does not update that space. `--native` is the pixel space `computer info --json` advertises (`size` / lastShot). Tab computers screenshot in device pixels; clicks are converted to CSS pixels with the tab's `devicePixelRatio` before they hit the page. A human can also click, scroll, and type in the live lightbox when the computer allows input; Escape releases. Frozen stills in the transcript never forward.\n\n## Poke (xdotool; chainable)\n\n```bash\ncomputer mousemove 120 80\ncomputer click 1 --at 120,80 # 1 left, 2 middle, 3 right\ncomputer click 120 80 # two numbers → coords, button 1\ncomputer drag 10 10 200 80\ncomputer scroll 0 -3 --at 120,80\ncomputer key ctrl+alt+Delete Return\ncomputer type hello world\ncomputer wait 400\ncomputer click 1 type hello # chain: click, then type\n```\n\nAnthropic aliases work the same: `left_click`, `right_click`, `mouse_move`, `left_click_drag`, `screenshot`, …\n\n## v86 guest\n\n```bash\nv86 start -n arch -state \"$TMPDIR/arch_state.bin.zst\" -fs9p https://i.copy.sh/arch/ -net virtio -m 512\ncomputer use v86:arch\ncomputer screenshot\ncomputer type \"uname -a\\n\"\ncomputer key Return\ncomputer record -V 5 guest.webm\n```\n\n`computer rm` unregisters `v86:<name>` and does **not** power the guest off. `v86 stop` / `kill <pid>` still does. `v86 serve` is retired — use `computer watch -c v86:<name>`.\n\n## Browser tab\n\n```bash\nplaywright-cli tab-list # pick a targetId that is not the SLICC app\ncomputer add tab <targetId> -n docs\ncomputer screenshot -c tab:<targetId>\ncomputer click 1 --at 100,80 type hello\n```\n\n`computer add tab` refuses SLICC app tabs (`sliccy.ai` leader, `?slicc=`, extension pages). Pass a URL or a CDP target id. Look at the screenshot and click what you see — screenshot-space `--at` lands on the visual target even when `devicePixelRatio` is not 1.\n\n## Display share (`screen`)\n\n```bash\ncomputer add screen -n desk # panel terminal or cone approval card\ncomputer screenshot -c screen:screen1\ncomputer record -V 10 clip.webm # timed clip from the live session\ncomputer rm screen:screen1 # stops the getDisplayMedia tracks\n```\n\n`computer add screen` needs a real user gesture (`getDisplayMedia`). Type it in the panel terminal, or run it from a cone tool call so an approval card can open the picker. `computer ls` marks a live share with `[display slot]`. Input (keyboard/mouse) is not supported. `computer info` reports the shared display's own resolution as `size` (e.g. `5120x2880`), learned from the first frame — it is `-` until something captures, and no consumer's `--size` changes it; read it to see how far `--size` can go before frames stop gaining detail. Screen share cannot come back after `jshd --enable` restore — there is no gesture at boot.\n\n`computer record` writes a clip to a VFS path (default `clip.webm`, max 60s). `screen` uses the live session recorder; v86/tab/url/jsh and a non-native `ssh` poll JPEG stills and pipe them through in-repo ffmpeg wasm (`-f image2pipe`). Default `--fps` is 2. A native-capture `ssh` Mac streams instead (see below).\n\n## Follower desktop (`ssh`)\n\n```bash\nhost # every follower, tagged [ssh] [computer] [playwright]\nssh --list # exec-capable tray followers only\ncomputer add ssh follower-abc -n desk # view-only\ncomputer screenshot\ncomputer add ssh follower-abc --allow-input\ncomputer click 1 --at 100,80 type hello\ncomputer add ssh mac-follower --sim UDID-1 --allow-input # iOS Simulator\ncomputer add ssh mac-follower --display 3 -n portrait # a second screen\n```\n\nProbes at add: native ScreenCaptureKit/CGEvent when the follower advertises `capabilities.computer` (Swift launcher `sliccstart-computer`); else `screencapture` + `cliclick` (macOS), `grim`/`scrot`/`import` + `xdotool`/`ydotool` (Linux), `xcrun simctl io <udid> screenshot` + `idb ui` (`--sim`). `--sim` always takes the simctl/idb path, even on a native-capable follower — native capture sees the Mac desktop, never the Simulator; a missing or shut-down udid fails the add (`simctl boot` it first).\n\nA Mac started with `slicc <join-url> follow --computer` shows as **one** entry in `host`, tagged `[ssh] [computer]` — the CLI and the headless Sliccstart it spawned are folded by a shared `hello.pairId`. Point `computer add ssh` at that one id and it picks ScreenCaptureKit automatically. **`host` is the roster to read**: an entry tagged `[computer]` can be looked at, whether it holds the screen itself or through its paired launcher. `ssh --list` shows the exec half only, because that is the list of targets that accept commands — a capture-only `sliccstart-computer` is addressable from `host`, never from `ssh`. Without `--computer` the same Mac appears twice (a `follower-…` tagged `[ssh]` and a `sliccstart-computer` tagged `[computer]`); either works, but only the native one captures under SLICC's own screen-recording permission. Frames come back base64 in ≤3 MiB chunks over tray-exec (or `computer.native.frame` when native). `--allow-input` is a sudo hop (`kind: command`) so a phone can answer with Face ID; `computer ls` shows `[input]` or `[view-only]`. The iOS follower itself is never a driven computer (a real iPhone is out of scope).\n\n**Live frames (native capture only).** `computer watch` and `computer record` on a native Mac consume one ScreenCaptureKit stream of the display the id names, rather than taking a fresh screenshot per frame. Two watchers share that one stream; it stops when the last one stops. While it runs, `computer screenshot` hands you the stream's newest frame — a fresh one-shot would replace the follower's live capturer and end the stream. After a click or key, the frame written is the first one that arrives after the action. Everything else (`ssh` without native capture, v86, tab, url, jsh) still polls.\n\n**Which screen (native capture only).** Default is the Mac's **main** display. `--display <n>` picks another: 1-based in the OS display order, the same numbering as `screencapture -D <n>` on that follower, so `screencapture -D 3` through the exec channel shows you what `--display 3` will capture. An out-of-range number fails with the attached displays listed (`display 7 is not attached — attached displays: 1=5120x2880 (main), …`), which is also the cheapest way to enumerate them. A picked display widens the id to `ssh:<runtime>:display:<n>`, so register one computer per screen to drive several at once. Native frames and `--size` are in real **pixels**, so `--size 2000` reaches 2000 px wide on a 2x display, not the 1440 point width.\n\nA Mac only advertises `capabilities.computer` while its Screen Recording grant is actually in place, so `computer add ssh` on an ungranted Mac names the missing grant and falls back to `screencapture` through the machine's exec follower instead of failing on the native path. Ticking the box in System Settings republishes the capability within seconds — no reconnect. A `--computer` Mac without Screen Recording is still **one** `host` entry, tagged `[ssh]` only; the launcher's MOTD naming the missing grant prints on its own line under the CLI's (in `host` and `ssh --list`). A follower whose MOTD mentions Accessibility can capture but not inject: `--allow-input` will be accepted and events will fail until that box is ticked too.\n\n## HTTP remote (`url`)\n\n```bash\ncomputer add url http://127.0.0.1:5710 -n demo\ncomputer screenshot\ncomputer text\ncomputer type hello\n```\n\nThe remote must answer `GET /computer` with a `ComputerDescriptor`. Screenshots are `GET /computer/screenshot`; optional `GET /computer/text` (404 means none); input is `POST /computer/input`. A trailing `/computer` on the base is stripped. Live frames use `WS /computer/frames` only when the descriptor advertises `frames: \"push\"`; a failed or closed socket falls back to screenshot polling. Otherwise `computer watch` polls. The in-tree reference is node-server `--computer-demo` (same port as the `/cdp` bridge).\n\n## jsh-hosted backend\n\nA durable `.jsh` can register a computer with `require('sliccy:computer').register(...)`. `register()` subscribes to host `computer-call` events, which keeps `jshd` alive. Optional `handlers.subscribe(fps, onFrame, maxWidth)` is the `computer watch` push path. Example: `/workspace/skills/jshd/examples/fake-computer.jsh`.\n";export{e as default};
|
|
1
|
+
var e="---\nname: computer\ndescription: |\n Use this when looking at and poking a screen with SLICC's `computer`\n shell command (xdotool grammar). Covers v86 guests (`v86:<name>`),\n browser tabs (`tab:<id>`), display share (`screen:<handle>`), follower\n desktops (`ssh:<runtimeId>`), HTTP remotes (`url:<host>`), jsh-hosted\n backends, screenshot-space coordinates, frozen JPEG frames, and chaining\n click/type/key.\nallowed-tools: bash\n---\n\n# computer — look at and poke a screen\n\n`computer` is the look-then-act loop for every registered screen. Prefer it over `v86 type|key|mouse|screenshot|text` once a guest is running. `v86 start` still boots the VM and registers `v86:<name>`.\n\n## Target\n\n```bash\ncomputer ls # ids, kinds, names, titles\ncomputer use v86:arch # default for later verbs\ncomputer info # descriptor for the current target\n```\n\nResolution order: `-c` / `--computer`, else `$COMPUTER`, else last `computer use`, else the only registered computer.\n\n`-c` takes an id, the `-n` name the computer was added under, or its current title — in that order. Prefer the **id** or the **name**: `ls` shows them in separate columns because `TITLE` is whatever the surface calls itself right now (a tab republishes `document.title` on every capture), while `NAME` is fixed for the life of the registration.\n\n## Look then act\n\nEvery poke writes a frozen JPEG and prints `target: <id>` then `screen: <path>`. The extension of that path follows the bytes, so it is `.jpg` in practice and `.png` only if a backend hands back PNG the runtime could not transcode — read the extension, do not assume it. The `target:` line is the resolved computer (`-c`, `$COMPUTER`, last `use`, or the only registered one) so a later `computer screenshot` without `-c` still binds the bash-row UI. That poke frame is a transcript side effect only — it does **not** redefine the coordinate space, so never read new coordinates off it (it can come back narrower and your clicks would map to the wrong place). Run a fresh `computer screenshot` before choosing coordinates for the next click. If the frozen frame cannot be captured, the poke still exits 0 (it already happened); do not retry the same click.\n\n```bash\ncomputer screenshot # JPEG; prints WxH → wxh (scale s)\ncomputer screenshot --size high out.jpg\ncomputer text # text-mode dump when the backend supports it\n```\n\n`--size` is `low` (256), `medium` (768, default), `high` (1536), or a max width. It is an upper bound: a frame is never widened past the computer's native size to reach it, and one that cannot be encoded that narrow arrives at native size rather than upscaled. Coordinates on later verbs are in that last `computer screenshot` (the last model-facing shot) unless `--native` — a poke's frozen frame does not update that space. `--native` is the pixel space `computer info --json` advertises (`size` / lastShot). Tab computers screenshot in device pixels; clicks are converted to CSS pixels with the tab's `devicePixelRatio` before they hit the page. A human can also click, scroll, and type in the live lightbox when the computer allows input; Escape releases. Frozen stills in the transcript never forward.\n\n## Poke (xdotool; chainable)\n\n```bash\ncomputer mousemove 120 80\ncomputer click 1 --at 120,80 # 1 left, 2 middle, 3 right\ncomputer click 120 80 # two numbers → coords, button 1\ncomputer drag 10 10 200 80\ncomputer scroll 0 -3 --at 120,80\ncomputer key ctrl+alt+Delete Return\ncomputer type hello world\ncomputer wait 400\ncomputer click 1 type hello # chain: click, then type\n```\n\nAnthropic aliases work the same: `left_click`, `right_click`, `mouse_move`, `left_click_drag`, `screenshot`, …\n\n## v86 guest\n\n```bash\nv86 start -n arch -state \"$TMPDIR/arch_state.bin.zst\" -fs9p https://i.copy.sh/arch/ -net virtio -m 512\ncomputer use v86:arch\ncomputer screenshot\ncomputer type \"uname -a\\n\"\ncomputer key Return\ncomputer record -V 5 guest.webm\n```\n\n`computer rm` unregisters `v86:<name>` and does **not** power the guest off. `v86 stop` / `kill <pid>` still does. `v86 serve` is retired — use `computer watch -c v86:<name>`.\n\n## Browser tab\n\n```bash\nplaywright-cli tab-list # pick a targetId that is not the SLICC app\ncomputer add tab <targetId> -n docs\ncomputer screenshot -c tab:<targetId>\ncomputer click 1 --at 100,80 type hello\n```\n\n`computer add tab` refuses SLICC app tabs (`sliccy.ai` leader, `?slicc=`, extension pages). Pass a URL or a CDP target id. Look at the screenshot and click what you see — screenshot-space `--at` lands on the visual target even when `devicePixelRatio` is not 1.\n\n## Display share (`screen`)\n\n```bash\ncomputer add screen -n desk # panel terminal or cone approval card\ncomputer screenshot -c screen:screen1\ncomputer record -V 10 clip.webm # timed clip from the live session\ncomputer rm screen:screen1 # stops the getDisplayMedia tracks\n```\n\n`computer add screen` needs a real user gesture (`getDisplayMedia`). Type it in the panel terminal, or run it from a cone tool call so an approval card can open the picker. `computer ls` marks a live share with `[display slot]`. Input (keyboard/mouse) is not supported. `computer info` reports the shared display's own resolution as `size` (e.g. `5120x2880`), learned from the first frame — it is `-` until something captures, and no consumer's `--size` changes it; read it to see how far `--size` can go before frames stop gaining detail. Screen share cannot come back after `jshd --enable` restore — there is no gesture at boot.\n\n`computer record` writes a clip to a VFS path (default `clip.webm`, max 60s). `screen` uses the live session recorder; v86/tab/url/jsh and a non-native `ssh` poll JPEG stills and pipe them through in-repo ffmpeg wasm (`-f image2pipe`). Default `--fps` is 2. A native-capture `ssh` Mac streams instead (see below). `--fps` is a target, not a promise: capture time counts against each interval, and a polled clip is timed from when its stills were actually taken, so it plays at real speed. The reported `durationMs` is the measured span (it can run past `-V` when one capture overruns the window); the output adds `N frames at X fps`, and `--json` adds `frames`, `fps`, `requestedFps`, plus `slow: true` when the backend could not keep up — lower `--fps` rather than trusting a slow clip's frame rate.\n\n## Follower desktop (`ssh`)\n\n```bash\nhost # every follower, tagged [ssh] [computer] [playwright]\nssh --list # exec-capable tray followers only\ncomputer add ssh follower-abc -n desk # view-only\ncomputer screenshot\ncomputer add ssh follower-abc --allow-input\ncomputer click 1 --at 100,80 type hello\ncomputer add ssh mac-follower --sim UDID-1 --allow-input # iOS Simulator\ncomputer add ssh mac-follower --display 3 -n portrait # a second screen\n```\n\nProbes at add: native ScreenCaptureKit/CGEvent when the follower advertises `capabilities.computer` (Swift launcher `sliccstart-computer`); else `screencapture` + `cliclick` (macOS), `grim`/`scrot`/`import` + `xdotool`/`ydotool` (Linux), `xcrun simctl io <udid> screenshot` + `idb ui` (`--sim`). `--sim` always takes the simctl/idb path, even on a native-capable follower — native capture sees the Mac desktop, never the Simulator; a missing or shut-down udid fails the add (`simctl boot` it first).\n\nA Mac started with `slicc <join-url> follow --computer` shows as **one** entry in `host`, tagged `[ssh] [computer]` — the CLI and the headless Sliccstart it spawned are folded by a shared `hello.pairId`. Point `computer add ssh` at that one id and it picks ScreenCaptureKit automatically. **`host` is the roster to read**: an entry tagged `[computer]` can be looked at, whether it holds the screen itself or through its paired launcher. `ssh --list` shows the exec half only, because that is the list of targets that accept commands — a capture-only `sliccstart-computer` is addressable from `host`, never from `ssh`. Without `--computer` the same Mac appears twice (a `follower-…` tagged `[ssh]` and a `sliccstart-computer` tagged `[computer]`); either works, but only the native one captures under SLICC's own screen-recording permission. Frames come back base64 in ≤3 MiB chunks over tray-exec (or `computer.native.frame` when native). `--allow-input` is a sudo hop (`kind: command`) so a phone can answer with Face ID; `computer ls` shows `[input]` or `[view-only]`. The iOS follower itself is never a driven computer (a real iPhone is out of scope).\n\n**Live frames (native capture only).** `computer watch` and `computer record` on a native Mac consume one ScreenCaptureKit stream of the display the id names, rather than taking a fresh screenshot per frame. Two watchers share that one stream; it stops when the last one stops. While it runs, `computer screenshot` hands you the stream's newest frame — a fresh one-shot would replace the follower's live capturer and end the stream. After a click or key, the frame written is the first one that arrives after the action. Everything else (`ssh` without native capture, v86, tab, url, jsh) still polls.\n\n**Which screen (native capture only).** Default is the Mac's **main** display. `--display <n>` picks another: 1-based in the OS display order, the same numbering as `screencapture -D <n>` on that follower, so `screencapture -D 3` through the exec channel shows you what `--display 3` will capture. An out-of-range number fails with the attached displays listed (`display 7 is not attached — attached displays: 1=5120x2880 (main), …`), which is also the cheapest way to enumerate them. A picked display widens the id to `ssh:<runtime>:display:<n>`, so register one computer per screen to drive several at once. Native frames and `--size` are in real **pixels**, so `--size 2000` reaches 2000 px wide on a 2x display, not the 1440 point width.\n\nA Mac only advertises `capabilities.computer` while its Screen Recording grant is actually in place, so `computer add ssh` on an ungranted Mac names the missing grant and falls back to `screencapture` through the machine's exec follower instead of failing on the native path. Ticking the box in System Settings republishes the capability within seconds — no reconnect. A `--computer` Mac without Screen Recording is still **one** `host` entry, tagged `[ssh]` only; the launcher's MOTD naming the missing grant prints on its own line under the CLI's (in `host` and `ssh --list`). A follower whose MOTD mentions Accessibility can capture but not inject: `--allow-input` will be accepted and events will fail until that box is ticked too.\n\n## HTTP remote (`url`)\n\n```bash\ncomputer add url http://127.0.0.1:5710 -n demo\ncomputer screenshot\ncomputer text\ncomputer type hello\n```\n\nThe remote must answer `GET /computer` with a `ComputerDescriptor`. Screenshots are `GET /computer/screenshot`; optional `GET /computer/text` (404 means none); input is `POST /computer/input`. A trailing `/computer` on the base is stripped. Live frames use `WS /computer/frames` only when the descriptor advertises `frames: \"push\"`; a failed or closed socket falls back to screenshot polling. Otherwise `computer watch` polls. The in-tree reference is node-server `--computer-demo` (same port as the `/cdp` bridge).\n\n## jsh-hosted backend\n\nA durable `.jsh` can register a computer with `require('sliccy:computer').register(...)`. `register()` subscribes to host `computer-call` events, which keeps `jshd` alive. Optional `handlers.subscribe(fps, onFrame, maxWidth)` is the `computer watch` push path. Example: `/workspace/skills/jshd/examples/fake-computer.jsh`.\n";export{e as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e="---\nname: playwright-cli\ndescription: |\n Use this whenever the user asks to browse, navigate, click, fill a form,\n scrape, take a screenshot, or otherwise interact with a web page. SLICC drives\n the browser through the `playwright-cli` shell command (also aliased as\n `playwright` and `puppeteer`). Read this BEFORE running any browser\n automation: every tab-operating command requires a `--tab` target id, and\n multi-agent tab handling has rules you must follow.\nallowed-tools: bash\n---\n\n# Browser Automation via playwright-cli\n\nUse `playwright-cli` (also aliased as `playwright` and `puppeteer`) via the bash tool for all browser automation.\n\n**Every tab-operating command requires `--tab=<targetId>`.** There is no implicit \"current tab\". Always specify which tab you're operating on.\n\n`playwright-cli <command> --help` prints that command's usage and does nothing else — safe to run before any action.\n\n## Quick Start\n\n```bash\n# 1. Open a page — note the targetId in the output\nplaywright-cli open https://example.com\n# Output: Opened https://example.com in new tab [targetId: E9A3F...]\n\n# 2. Take a snapshot to see the page structure and get element refs\nplaywright-cli snapshot --tab=E9A3F\n\n# 3. Interact using refs from the snapshot (e.g. e5, e12)\nplaywright-cli click --tab=E9A3F e5\nplaywright-cli fill --tab=E9A3F e12 \"hello world\"\n\n# 4. Re-snapshot after interactions (refs change)\nplaywright-cli snapshot --tab=E9A3F\n```\n\n## Tab IDs\n\n- `tab-list` shows all tabs with their targetIds. The user's active tab is marked `(active)`.\n- `tab-new` / `open` return the new tab's targetId — capture it for subsequent commands.\n- Use `--tab=<targetId>` on ALL commands that operate on a tab.\n\n## Frame IDs\n\n- `frames --tab=<targetId>` lists frame IDs. A frame ID is not a tab target ID; never pass it to `--tab`.\n- Use `--tab=<targetId> --frame=<frameId>` with `eval`, `eval-file`, or `snapshot` to target a child frame, including cross-origin frames.\n- A frame-scoped `snapshot` prints only that frame's accessibility subtree. Its frame-prefixed refs work with `click`, `fill`, `dblclick`, `hover`, `select`, `check`, and `uncheck`; do not pass `--frame` to those commands. Other ref commands require refs from a top-level snapshot.\n\n## Common Failure Modes\n\n- `--tab <targetId> is required` — you forgot `--tab=<id>`. Run `tab-list` to get IDs.\n- `is a frame ID, not a tab target ID` — keep the owning tab's targetId in `--tab` and pass the frame ID with `--frame`.\n- `No snapshot available` — run `snapshot --tab=<id>` before using refs.\n- `unknown flag \"--x\"` / `unexpected argument \"y\"` — every subcommand rejects arguments it does not\n support, so an exit code of 0 means everything you passed was honoured. Check `<verb> --help`.\n- For free-text payloads that start with `-` (e.g. `fill`/`type`/`eval`), put `--` before the text:\n `fill --tab=<id> e1 -- --dash-looking`.\n- `is not an element ref` — a screenshot's positional is a main-frame element ref (`e5`); the output\n path is `--filename=<path>`. Frame-prefixed refs (`f1e5`) clip only with `click`-family commands.\n- Refs are tied to **one tab + one snapshot**. They do not carry across tabs, navigations, or reloads.\n\n## Element Refs\n\nSnapshots assign short ref IDs (`e1`, `e2`, ...) to interactive elements. Use these refs with `click`, `fill`, `dblclick`, `hover`, `select`, `check`, `uncheck`, `drag`, `upload`, `drop`, and `screenshot`.\n\nRefs are invalidated after any state-changing command. Always re-snapshot to get fresh refs.\n\n## Commands\n\nAll commands below that operate on a tab require `--tab=<targetId>`.\n\n### Core\n\n```bash\nplaywright-cli open [url] [--foreground] [--mobile] # Open tab (background by default), returns targetId\nplaywright-cli tab-new [url] [--foreground] [--mobile] # Same as open\nplaywright-cli tab-close --tab=<id> # Close tab (alias: close)\nplaywright-cli goto --tab=<id> <url> # Navigate tab\nplaywright-cli navigate --tab=<id> <url> # Alias for goto\nplaywright-cli snapshot --tab=<id> [--frame=<frameId>] [--no-iframes] [--filename=path] [--depth=<n>] [--boxes] # Tab tree or one frame subtree; --depth limits nesting, --boxes adds [box=x,y,w,h] rects\nplaywright-cli find --tab=<id> <text> # Search the snapshot for text (case-insensitive), returns matching lines + context\nplaywright-cli find --tab=<id> --regex=<re> # Same, with a regexp (use either text or --regex, not both)\nplaywright-cli eval --tab=<id> [--frame=<frameId>] <expression> [--filename=path|--output=path] # Evaluate JS in a tab/frame, incl. top-level await/return; use `--` before an expression that starts with `-`\nplaywright-cli eval-file --tab=<id> [--frame=<frameId>] <vfs-path> [--output=path|--filename=path] # Evaluate JS from a VFS file in a tab/frame (top-level await/return supported)\nplaywright-cli frames --tab=<id> # List frame IDs for --frame (not --tab)\nplaywright-cli resize --tab=<id> <width> <height> # Resize viewport\n```\n\n`--foreground` (or `--fg`) opens the new tab **in the foreground** — it brings the\nnew tab to the front and switches the user's visible/active tab to it, instead of\nopening in the background.\n\n**Bringing a tab to the foreground (activate / focus / raise / switch to a tab):**\n\n- **New tab** → `open <url> --foreground` (or `tab-new <url> --fg`).\n- **Existing tab** → `tab-select <index>` (1-based index from `tab-list`).\n\n`--mobile` opens the tab with generic mobile-device emulation (viewport,\ndevice pixel ratio, touch, Android Chrome UA), sticky for the tab's lifetime —\na later `resize` changes only the dimensions and keeps the mobile identity.\nMobile pages are usually lighter, which saves tokens.\n\nThese two are the ONLY ways to change which tab is in front. There is no\n`activate`, `bring-to-front`, or `focus` subcommand, and `eval`-ing\n`window.focus()` does NOT work (browsers block a page from stealing foreground).\nIf a tab didn't come to the front, re-run `tab-select` / `--foreground` — do not\nfall back to `window.focus()`.\n\n### Interaction\n\n```bash\nplaywright-cli click --tab=<id> <ref> [--modifiers=Shift,Control,Alt,Meta] # Click element\nplaywright-cli dblclick --tab=<id> <ref> [button] [--modifiers=...] # Double-click\nplaywright-cli fill --tab=<id> <ref> <text> [--submit] # Clear input + type text (--submit presses Enter); use `--` before text that starts with `-`\nplaywright-cli type --tab=<id> <text> [--submit] # Type into focused element (--submit presses Enter); use `--` before text that starts with `-`\nplaywright-cli hover --tab=<id> <ref> # Hover over element\nplaywright-cli select --tab=<id> <ref> <value> # Select dropdown value\nplaywright-cli check --tab=<id> <ref> # Check checkbox/radio\nplaywright-cli uncheck --tab=<id> <ref> # Uncheck checkbox/radio\nplaywright-cli drag --tab=<id> <startRef> <endRef> # Drag and drop\nplaywright-cli upload --tab=<id> [ref] <file> [file...] # Upload VFS files to file input as raw bytes (optional snapshot ref targets hidden inputs)\nplaywright-cli drop --tab=<id> <ref> [--path=<vfs-path>] [--data=<mime/type=value>] # Drop files/data onto element (--path drops raw bytes)\nplaywright-cli dialog-accept --tab=<id> [text] # Accept JS dialog\nplaywright-cli dialog-dismiss --tab=<id> # Dismiss JS dialog\n```\n\n### Keyboard\n\n```bash\nplaywright-cli press --tab=<id> <key> # Press key (keyDown + keyUp, e.g. Enter, Tab, Escape)\nplaywright-cli keydown --tab=<id> <key> # Hold key down (no paired keyUp)\nplaywright-cli keyup --tab=<id> <key> # Release held key (no paired keyDown)\n```\n\n### Mouse\n\n```bash\nplaywright-cli mousemove --tab=<id> <x> <y> # Move mouse to coordinates (negative coords ok, e.g. -10 20)\nplaywright-cli mousedown --tab=<id> [button] # Press mouse button (left/right/middle, default: left)\nplaywright-cli mouseup --tab=<id> [button] # Release mouse button\nplaywright-cli mousewheel --tab=<id> <dx> <dy> # Scroll mouse wheel (negative deltas ok, e.g. 0 -300)\n```\n\n### Navigation\n\n```bash\nplaywright-cli go-back --tab=<id> # history.back()\nplaywright-cli go-forward --tab=<id> # history.forward()\nplaywright-cli reload --tab=<id> # Reload page\n```\n\n### Teleport\n\n```bash\nplaywright-cli teleport --tab=<id> --start=<regex> --return=<regex> [--timeout=<s>]\nplaywright-cli teleport --list # List available follower runtimes\nplaywright-cli teleport --off --tab=<id> # Cancel a teleport on this tab\nplaywright-cli open <url> --teleport-start=<regex> --teleport-return=<regex>\nplaywright-cli goto --tab=<id> <url> --teleport-start=<regex> --teleport-return=<regex>\n```\n\nTeleport is for leader/follower tray auth handoffs. Scoped to a specific tab — only commands targeting the teleporting tab are blocked; other tabs remain operational.\n\n`--runtime` is optional: without it, teleport picks the most recently active eligible follower (preferring standalone floats). Only followers that can actually serve cookies are eligible — cherry hosts, exec-only CLI followers, and any target advertising `network: false` are excluded, so a teleport fails fast instead of \"succeeding\" with zero cookies.\n\n### Screenshots\n\n```bash\nplaywright-cli screenshot --tab=<id> # Save to $TMPDIR/screenshot-<ts>.png\nplaywright-cli screenshot --tab=<id> --filename=page.png # Save to custom path\nplaywright-cli screenshot --tab=<id> e5 # Clip to an element (positional = MAIN-FRAME ref, not a path)\nplaywright-cli screenshot --tab=<id> --fullPage # Full scrollable page (alias: --full-page)\nplaywright-cli screenshot --tab=<id> --max-width=800 # Downscale to a max width (png output only)\nplaywright-cli screenshot --tab=<id> --type=jpeg # png (default) / jpeg / webp; inferred from --filename extension\nplaywright-cli screenshot --tab=<id> --hires # Capture in device pixels (honors device pixel ratio)\n```\n\nViewport screenshots (default, `--max-width`, `--hires`) honor the tab's current scroll position. `--fullPage` captures from the document origin.\n\nAn element screenshot (`screenshot e5`) returns **that element's crop or fails**\n(exit 1) — typically because the snapshot went stale after a navigation or\nlayout change. Re-run `snapshot` and retry with a fresh ref; it never silently\nsubstitutes a full-viewport frame.\n\nA `resize` sticks to its tab: the viewport override is re-applied automatically\nwhenever the tab is re-attached, so another driver switching tabs cannot reset\nit.\n\nAll playwright-cli commands share one browser, and locking is **per tab**:\ncommands on different tabs run in parallel; commands on the same tab serialize.\nA slow or hung navigation stalls only its own tab. A few genuinely global\noperations (`tab-select`, `--foreground`, and the attach that points the bridge\nat a tab) still take a bridge-wide lock, but they are short and never wrap a\npage wait. When callers queue up, commands may emit a\n`note: browser bridge contended — ...` line on stderr with the total lock wait\nand queue depth; the note says whether the wait was on **this tab** or\n**bridge-wide**. Either way it is back-off guidance — stagger callers or give\neach one its own tab, never re-run the command; it already ran, it was just\nslow to get the lock.\n\n**Giving up cancels the command.** When your `bash` call stops waiting — the\nturn is cancelled, `timeout` fires, or someone runs `kill <pid>` — the\nplaywright-cli command is cancelled too instead of running on for a caller that\nis gone: a queued command drops out of the tab's queue without taking the lock,\na `goto` waiting on a load event and a `waitForSelector` poll stop at once, and\na multi-step command stops before its next CDP round trip. Such a command exits\n**130** with a stderr line naming the step it stopped at, never 0.\n\nTwo consequences for you:\n\n- A cancelled command may have **half-landed**. The CDP request already on the\n wire cannot be recalled, so keys may be typed or a click delivered before the\n stop. `snapshot` the tab and continue from what you see rather than blindly\n re-running — same rule as a mid-command session reset.\n- `background_after` alone does **not** cancel; it detaches, and the command\n keeps running as a background job (that is what makes a long `goto` usable).\n Use `timeout` or `kill <pid>` when you actually want it stopped.\n\n**Stale session.** The bridge re-attaches automatically after a Chrome reset;\nthere is no need to reload the tab or open a new one. Two error shapes:\n\n- `Session with given id not found` — the bridge already re-attached; re-run\n the command.\n- `… was reset mid-command … the outcome is unknown` — part of the command may\n have landed (keys typed, a click delivered) before the session died. Do NOT\n blindly re-run it: `snapshot` the tab first and continue from what you see.\n\n### Save As\n\n```bash\nplaywright-cli pdf --tab=<id> [--filename=path] # Save page as PDF (not available in extension mode)\n```\n\n### Viewing pages and screenshots yourself\n\nThe browser displays things to the human; `open --view` is what lets _you_ see them. **But viewing screenshots is a last resort for the cone — every image you load eats a large chunk of the context window** (a single 1280×800 PNG can run 1500+ tokens, full-page screenshots much more). Reach for cheaper signals first:\n\n1. **`playwright-cli snapshot --tab=<id>`** — text accessibility tree. Use this first; it answers \"what's on the page\" for almost all verification tasks at a tiny fraction of the token cost. On a big page, `find --tab=<id> <text>` greps the snapshot and returns just the matching lines with context, and `snapshot --depth=<n>` caps the tree — both much cheaper than the full tree.\n2. **`eval` against the DOM** — when you need a specific value (`document.title`, an attribute, computed style), `eval` it. Don't screenshot for facts you can extract.\n3. **Delegate visual inspection to a scoop.** If the cone genuinely needs vision (layout regression, render fidelity, \"does this look right\"), spawn a scoop to take and view the screenshot — the scoop's context absorbs the tokens, and you receive its summary back. The cone's window stays clean for orchestration.\n4. **`open --view` in the cone** — only when the cone itself must see pixels for its current decision and steps 1–3 won't do.\n\n**What you CAN see:**\n\n- `open --view <path>` — reads an image from the VFS and returns it. Works with PNG, JPEG, GIF, WebP, SVG.\n- `playwright-cli screenshot --tab=<id>` + `open --view <path>` — screenshot a tab, then view it.\n- `screencapture --view screenshot.png` — capture the user's screen via browser screen sharing.\n- `screencapture --video -V 10 clip.webm` — record a timed screen/window/tab clip (WebM).\n- `playwright-cli snapshot --tab=<id>` — accessibility tree (text). Use to verify content without vision.\n\n**What only the human sees:**\n\n- `serve <dir>` — opens an app directory in a browser tab (read-only preview).\n- Preview URLs survive authenticated tray roves without changing their original directory, entry file, or bridge permissions. A handoff can briefly return `503`; retry the same pending rove rather than minting another target. Bridge tabs reconnect automatically (their connection IDs may change). Revoked previews stay revoked, and persistent snapshots keep their original expiry rather than receiving a fresh TTL.\n- Persistent uploads retry safely only with the same file bytes and MIME type. Body/R2-write waits time out after 30 seconds. Unknown writes retain bounded cleanup bookkeeping (up to eight unresolved uploads per preview); expiry/revoke never makes such a preview serve again, even if a delayed write finishes later.\n- `serve --ttl 30d <dir>` — uploads an immutable snapshot that works without the leader. TTL units are whole `m`, `h`, `d`, or `w`, capped at 30 days. It implies `--no-bridge`, conflicts with `--bridge`/`--max-tabs`, and is limited to 1,000 files, 25 MiB per file, and 50 MiB total. Use `serve --list` for mode/expiry and `serve --stop <token>` to revoke it early.\n- **`serve` limits — plan for them:** a live-preview file over 25 MiB is served only through Range requests (a plain GET answers 413; media elements use ranges, so no splitting is needed), and `--ttl` files must be ≤ 25 MiB. A tray holds at most **10 `--ttl` snapshots** (uploads in progress count); live previews have no quota. A live preview is deleted once the leader has been disconnected for 5 minutes; a snapshot lasts until its TTL. Each `serve` prints `Preview token: …`; `serve --stop <token-or-url>` snapshots you no longer need, and on `Snapshot limit reached` run `serve --list` and stop old ones.\n- `serve --bridge <dir>` — opens a **driveable** preview whose visitors auto-connect as live synthetic-CDP targets you can navigate/click/evaluate/screenshot via playwright. **Security: opt-in only; cross-subdomain cookie risk accepted (host-only cookies isolated; `Domain=.sliccy.now` cookies readable across previews).** Flags: `--max-tabs <N>` (default 20), `--quiet` (suppress the first-visit announcement), `--no-bridge` (force read-only), `--stop <token>` (revoke + delete webhook). Use `serve --logs [<token>] [--lines <N>]` to inspect leader-memory-only connect/disconnect records without emitting a lick or waking the cone. `serve --truncate [<token>]` clears matching records **and re-arms the first-visit announcement**, so the next visit announces once. Visitor page API: `window.slicc.emit(name, detail?)` (fires webhook lick on cone), `window.slicc.on(name, cb)` (subscribes to CustomEvents you dispatch).\n- **Media in `serve` previews plays through Range requests:** live previews answer `Range` in ≤ 8 MiB windows, so `<video>`/`<audio>` larger than 25 MiB play and seek without splitting; only a plain full-file GET of such a file gets 413. `--ttl` snapshots support Range too (their files stay ≤ 25 MiB).\n- `open <path>` (no flags) — opens a file in a browser tab.\n- `imgcat <path>` — displays an image in the terminal preview.\n\n**Workflow to verify a page (when vision is actually required):**\n\n1. `serve /workspace/app` — open the app (the human sees it).\n2. `playwright-cli tab-list` — find the tab by URL, note the targetId.\n3. `playwright-cli snapshot --tab=<id>` — required before screenshot, and often answers your question on its own.\n4. `playwright-cli screenshot --tab=<id> --filename=\"$TMPDIR/shot.png\"` — consider `--max-width` to keep the file small.\n5. `open --view \"$TMPDIR/shot.png\"` — now you can see it. Strongly prefer doing this from a scoop, not the cone.\n\n**Don't:**\n\n- Default to screenshots when a snapshot would do.\n- `read_file` on a PNG or base64-encode to view images.\n- `imgcat` or `cat` on screenshots expecting to see them.\n- Open a screenshot then screenshot that tab.\n- Use `eval` to check the active tab — use `tab-list`.\n\n### Tab Management\n\n```bash\nplaywright-cli tab-list # List tabs with targetIds + (active) marker\nplaywright-cli tab-new [url] # New tab, returns targetId\nplaywright-cli tab-close --tab=<id> # Close specific tab\nplaywright-cli tab-select <index> # Select (bring to front) tab by 1-based index\n```\n\n### Cookies\n\n```bash\nplaywright-cli cookie-list --tab=<id> [--domain=<d>] [--path=<p>] # List cookies (filter by domain/path)\nplaywright-cli cookie-get --tab=<id> <name> # Get cookie\nplaywright-cli cookie-set --tab=<id> <name> <value> [--sameSite=Strict|Lax|None] [other flags] # Set cookie\nplaywright-cli cookie-delete --tab=<id> <name> [--domain= --path=] # Delete cookie\nplaywright-cli cookie-clear --tab=<id> # Clear all cookies\n```\n\n### Storage\n\n```bash\nplaywright-cli localstorage-list --tab=<id>\nplaywright-cli localstorage-get --tab=<id> <key>\nplaywright-cli localstorage-set --tab=<id> <key> <value>\nplaywright-cli localstorage-delete --tab=<id> <key>\nplaywright-cli localstorage-clear --tab=<id>\n# Same pattern for sessionstorage-*\nplaywright-cli state-save --tab=<id> [filename|--filename=path] # Save cookies + localStorage to JSON\nplaywright-cli state-load --tab=<id> <filename> # Restore from state file\n```\n\n### Network\n\n```bash\nplaywright-cli console --tab=<id> [min-level] [--clear] # List console messages (debug/log/info/warning/error)\nplaywright-cli requests --tab=<id> [--static] [--filter=<regex>] [--clear] # List network requests\nplaywright-cli request --tab=<id> <index> [--filename=path] # Full request details\nplaywright-cli request-headers --tab=<id> <index> # Request headers only\nplaywright-cli request-body --tab=<id> <index> # Request body only\nplaywright-cli response-headers --tab=<id> <index> # Response headers only\nplaywright-cli response-body --tab=<id> <index> [--filename=path] # Response body (--filename saves the exact bytes)\nplaywright-cli network-state-set --tab=<id> <online|offline> # Toggle network state\nplaywright-cli route --tab=<id> <pattern> [--status=N] [--body=text] [--content-type=type] [--header=name:value]\nplaywright-cli route-list --tab=<id> # List active routes\nplaywright-cli unroute --tab=<id> [pattern] # Remove routes (all if no pattern)\n```\n\n### DevTools\n\n```bash\nplaywright-cli generate-locator --tab=<id> <ref> # Generate Playwright locator string for element\nplaywright-cli highlight --tab=<id> <ref> [--style=<css>] # Highlight element with visual overlay\nplaywright-cli highlight --tab=<id> --hide [ref] # Remove highlight (all if no ref)\n```\n\n### HAR Recording\n\n```bash\nplaywright-cli record [url] [--filter=<js-expr>] # Open tab with network recording\nplaywright-cli stop-recording <recordingId> # Stop and save HAR\n```\n\n## Multi-Agent Tab Behavior\n\n**All agents (cone + scoops) share the same tab namespace.** There is no tab isolation.\n\n- `tab-list` shows **every** tab from every agent — yours, the cone's, other scoops'. The list can be noisy.\n- Any agent can `eval`, `snapshot`, or `close` any tab — there are no ownership checks.\n- Tab counts fluctuate as other agents open and close tabs concurrently.\n\n**Best practices for scoops:**\n\n1. **Track your own tab IDs.** When you open a tab, capture the targetId and store it. Don't rely on `tab-list` to find your tabs later — other agents' tabs will be mixed in.\n\n ```bash\n # Open and capture the ID\n playwright-cli tab-new https://example.com\n # Output: Opened https://example.com in new tab [targetId: ABC123...]\n # Use ABC123 for all subsequent commands on this tab\n ```\n\n2. **NEVER close tabs you didn't open.** Tabs you don't recognize belong to the **user** or other agents. User tabs are off-limits unless the user explicitly asks you to close them. Only close tabs whose targetId you captured from your own `tab-new` / `open` calls.\n\n3. **Handle \"tab not found\" gracefully.** Another agent might close a tab between your `tab-list` and your command. If you get `Error: No tab with id`, the tab is gone — move on.\n\n4. **Don't depend on tab count or ordering.** Other agents are opening/closing tabs concurrently. Use targetIds, not positional logic.\n\n5. **Clean up when done.** Close all tabs you opened before finishing. Include this in every scoop brief:\n _\"Close each tab with `playwright-cli tab-close --tab=<id>` when done.\"_\n\n## Tips\n\n- **Refs change after every interaction** — always re-snapshot before clicking or filling.\n- `open` and `tab-new` open tabs in the **background** by default. Capture the targetId from the output. To open in the **foreground** add `--foreground`/`--fg`; to raise an **already-open** tab use `tab-select <index>`.\n- After `click`, `fill`, `goto`, `go-back`, `go-forward`, `reload`, `select`, `check`, `uncheck`, `drag`, or `dialog-*`, take a fresh `snapshot --tab=<id>` before using refs again.\n- Unexpected JavaScript dialogs are auto-dismissed on attached pages.\n- Use `eval --tab=<id>` for DOM operations not covered by built-in commands; save results with `--filename=path`.\n- The SLICC app tab and Chrome internal UI tabs are automatically excluded from `tab-list`.\n- `fill` clears and types into regular inputs, textareas, and `contenteditable` elements. Use `--submit` to press Enter after. If the text or an `eval` expression starts with `-`, put `--` before it so it is not parsed as a flag.\n- Negative numbers for `mousewheel` / `mousemove` are positionals (no `--` needed): `mousewheel --tab=<id> 0 -300`.\n- `drop --path=<vfs-path>` injects the file as **raw bytes**; a UTF-8 text round-trip would replace every byte >= 0x80 with `EF BF BD`, so mp4/zip/jpg drops stay byte-exact.\n- Screenshots default to `$TMPDIR/screenshot-<timestamp>.png` — your own scratch directory, not the shared root. Use `--filename=path` to save elsewhere —\n `screenshot <path>` is an error, because that slot is the element ref.\n- Unsupported flags and extra positionals are rejected per subcommand, so a probe that exits 0 means\n the flag was honoured — nothing is silently dropped.\n- Use `keydown`/`keyup` for holding modifier keys (e.g. Shift+click: `keydown Shift`, `click`, `keyup Shift`).\n- Use `requests` + `response-body` to inspect XHR/fetch responses; `route` to mock or block them.\n- **Calling the app's own backend? Use `curlwright`, not `eval-file`.** It is curl's flags run by a `fetch()` inside the tab, so cookies, origin and session come along: `curlwright -s -X POST https://app.example.com/api/items -H 'X-CSRF-Token: abc' -d '{\"name\":\"x\"}' --tab=<id>`. `-o <file>` writes a **byte-exact** body, which `eval-file` cannot do at all — that is the only way to pull a binary response out of a page. Without `--tab` it uses the tab already on that origin. `curlwright --help` for the flag list. It cancels the same way playwright-cli does — giving up exits **130** and writes no `-o` file for a response nobody is reading — but the in-page `fetch` is bounded by `-m`, not by the cancellation, so a cancelled request may still have reached the server.\n- `state-save` / `state-load` persist auth state (cookies + localStorage) across sessions.\n- `pdf` saves a print-layout PDF; not available in extension mode.\n- `upload` injects VFS files as **raw bytes** (a UTF-8 text round-trip would replace every byte ≥ 0x80 with `EF BF BD`). A leading `eN` / `fNeN` token is a snapshot ref, never a filename — run `snapshot` first; without a ref, focus the file input first. To upload a file named `e2`, pass `./e2` or an absolute path.\n\n## Low-level CDP escape hatch\n\nFor raw Chrome DevTools Protocol calls that `playwright-cli` doesn't wrap, send JSON-RPC directly over WebSocket with `websocat`:\n\n```bash\n# Discover the page's debug socket URL via the CDP HTTP endpoint\ncurl -s http://127.0.0.1:9222/json | jq -r '.[0].webSocketDebuggerUrl'\n\n# Send a single CDP method, receive the response, exit\necho 'Page.navigate {\"url\":\"https://example.com\"}' \\\n | websocat -1 --jsonrpc --jsonrpc-omit-jsonrpc ws://127.0.0.1:9222/devtools/page/<id>\n```\n\nRun `websocat --help` for the full flag list. Use this only when `playwright-cli` has no wrapper for the CDP method you need.\n";export{e as default};
|
|
1
|
+
var e="---\nname: playwright-cli\ndescription: |\n Use this whenever the user asks to browse, navigate, click, fill a form,\n scrape, take a screenshot, or otherwise interact with a web page. SLICC drives\n the browser through the `playwright-cli` shell command (also aliased as\n `playwright` and `puppeteer`). Read this BEFORE running any browser\n automation: every tab-operating command requires a `--tab` target id, and\n multi-agent tab handling has rules you must follow.\nallowed-tools: bash\n---\n\n# Browser Automation via playwright-cli\n\nUse `playwright-cli` (also aliased as `playwright` and `puppeteer`) via the bash tool for all browser automation.\n\n**Every tab-operating command requires `--tab=<targetId>`.** There is no implicit \"current tab\". Always specify which tab you're operating on.\n\n`playwright-cli <command> --help` prints that command's usage and does nothing else — safe to run before any action.\n\n## Quick Start\n\n```bash\n# 1. Open a page — note the targetId in the output\nplaywright-cli open https://example.com\n# Output: Opened https://example.com in new tab [targetId: E9A3F...]\n\n# 2. Take a snapshot to see the page structure and get element refs\nplaywright-cli snapshot --tab=E9A3F\n\n# 3. Interact using refs from the snapshot (e.g. e5, e12)\nplaywright-cli click --tab=E9A3F e5\nplaywright-cli fill --tab=E9A3F e12 \"hello world\"\n\n# 4. Re-snapshot after interactions (refs change)\nplaywright-cli snapshot --tab=E9A3F\n```\n\n## Tab IDs\n\n- `tab-list` shows all tabs with their targetIds. The user's active tab is marked `(active)`.\n- `tab-new` / `open` return the new tab's targetId — capture it for subsequent commands.\n- Use `--tab=<targetId>` on ALL commands that operate on a tab.\n\n## Frame IDs\n\n- `frames --tab=<targetId>` lists frame IDs. A frame ID is not a tab target ID; never pass it to `--tab`.\n- Use `--tab=<targetId> --frame=<frameId>` with `eval`, `eval-file`, or `snapshot` to target a child frame, including cross-origin frames.\n- A frame-scoped `snapshot` prints only that frame's accessibility subtree. Its frame-prefixed refs work with `click`, `fill`, `dblclick`, `hover`, `select`, `check`, and `uncheck`; do not pass `--frame` to those commands. Other ref commands require refs from a top-level snapshot.\n\n## Common Failure Modes\n\n- `--tab <targetId> is required` — you forgot `--tab=<id>`. Run `tab-list` to get IDs.\n- `is a frame ID, not a tab target ID` — keep the owning tab's targetId in `--tab` and pass the frame ID with `--frame`.\n- `No snapshot available` — run `snapshot --tab=<id>` before using refs.\n- `unknown flag \"--x\"` / `unexpected argument \"y\"` — every subcommand rejects arguments it does not\n support, so an exit code of 0 means everything you passed was honoured. Check `<verb> --help`.\n- For free-text payloads that start with `-` (e.g. `fill`/`type`/`eval`), put `--` before the text:\n `fill --tab=<id> e1 -- --dash-looking`.\n- `is not an element ref` — a screenshot's positional is a main-frame element ref (`e5`); the output\n path is `--filename=<path>`. Frame-prefixed refs (`f1e5`) clip only with `click`-family commands.\n- Refs are tied to **one tab + one snapshot**. They do not carry across tabs, navigations, or reloads.\n\n## Element Refs\n\nSnapshots assign short ref IDs (`e1`, `e2`, ...) to interactive elements. Use these refs with `click`, `fill`, `dblclick`, `hover`, `select`, `check`, `uncheck`, `drag`, `upload`, `drop`, and `screenshot`.\n\nRefs are invalidated after any state-changing command. Always re-snapshot to get fresh refs.\n\n## Commands\n\nAll commands below that operate on a tab require `--tab=<targetId>`.\n\n### Core\n\n```bash\nplaywright-cli open [url] [--foreground] [--mobile] # Open tab (background by default), returns targetId\nplaywright-cli tab-new [url] [--foreground] [--mobile] # Same as open\nplaywright-cli tab-close --tab=<id> # Close tab (alias: close)\nplaywright-cli goto --tab=<id> <url> # Navigate tab\nplaywright-cli navigate --tab=<id> <url> # Alias for goto\nplaywright-cli snapshot --tab=<id> [--frame=<frameId>] [--no-iframes] [--filename=path] [--depth=<n>] [--boxes] # Tab tree or one frame subtree; --depth limits nesting, --boxes adds [box=x,y,w,h] rects\nplaywright-cli find --tab=<id> <text> # Search the snapshot for text (case-insensitive), returns matching lines + context\nplaywright-cli find --tab=<id> --regex=<re> # Same, with a regexp (use either text or --regex, not both)\nplaywright-cli eval --tab=<id> [--frame=<frameId>] <expression> [--filename=path|--output=path] # Evaluate JS in a tab/frame, incl. top-level await/return; use `--` before an expression that starts with `-`\nplaywright-cli eval-file --tab=<id> [--frame=<frameId>] <vfs-path> [--output=path|--filename=path] # Evaluate JS from a VFS file in a tab/frame (top-level await/return supported)\nplaywright-cli frames --tab=<id> # List frame IDs for --frame (not --tab)\nplaywright-cli resize --tab=<id> <width> <height> # Resize viewport\n```\n\n`--foreground` (or `--fg`) opens the new tab **in the foreground** — it brings the\nnew tab to the front and switches the user's visible/active tab to it, instead of\nopening in the background.\n\n**Bringing a tab to the foreground (activate / focus / raise / switch to a tab):**\n\n- **New tab** → `open <url> --foreground` (or `tab-new <url> --fg`).\n- **Existing tab** → `tab-select <index>` (1-based index from `tab-list`).\n\n`--mobile` opens the tab with generic mobile-device emulation (viewport,\ndevice pixel ratio, touch, Android Chrome UA), sticky for the tab's lifetime —\na later `resize` changes only the dimensions and keeps the mobile identity.\nMobile pages are usually lighter, which saves tokens.\n\nThese two are the ONLY ways to change which tab is in front. There is no\n`activate`, `bring-to-front`, or `focus` subcommand, and `eval`-ing\n`window.focus()` does NOT work (browsers block a page from stealing foreground).\nIf a tab didn't come to the front, re-run `tab-select` / `--foreground` — do not\nfall back to `window.focus()`.\n\n### Interaction\n\n```bash\nplaywright-cli click --tab=<id> <ref> [--modifiers=Shift,Control,Alt,Meta] # Click element\nplaywright-cli dblclick --tab=<id> <ref> [button] [--modifiers=...] # Double-click\nplaywright-cli fill --tab=<id> <ref> <text> [--submit] # Clear input + type text (--submit presses Enter); use `--` before text that starts with `-`\nplaywright-cli type --tab=<id> <text> [--submit] # Type into focused element (--submit presses Enter); use `--` before text that starts with `-`\nplaywright-cli hover --tab=<id> <ref> # Hover over element\nplaywright-cli select --tab=<id> <ref> <value> # Select dropdown value\nplaywright-cli check --tab=<id> <ref> # Check checkbox/radio\nplaywright-cli uncheck --tab=<id> <ref> # Uncheck checkbox/radio\nplaywright-cli drag --tab=<id> <startRef> <endRef> # Drag and drop\nplaywright-cli upload --tab=<id> [ref] <file> [file...] # Upload VFS files to file input as raw bytes (optional snapshot ref targets hidden inputs)\nplaywright-cli drop --tab=<id> <ref> [--path=<vfs-path>] [--data=<mime/type=value>] # Drop files/data onto element (--path drops raw bytes)\nplaywright-cli dialog-accept --tab=<id> [text] # Accept JS dialog\nplaywright-cli dialog-dismiss --tab=<id> # Dismiss JS dialog\n```\n\n### Keyboard\n\n```bash\nplaywright-cli press --tab=<id> <key> # Press key (keyDown + keyUp, e.g. Enter, Tab, Escape)\nplaywright-cli keydown --tab=<id> <key> # Hold key down (no paired keyUp)\nplaywright-cli keyup --tab=<id> <key> # Release held key (no paired keyDown)\n```\n\n### Mouse\n\n```bash\nplaywright-cli mousemove --tab=<id> <x> <y> # Move mouse to coordinates (negative coords ok, e.g. -10 20)\nplaywright-cli mousedown --tab=<id> [button] # Press mouse button (left/right/middle, default: left)\nplaywright-cli mouseup --tab=<id> [button] # Release mouse button\nplaywright-cli mousewheel --tab=<id> <dx> <dy> # Scroll mouse wheel (negative deltas ok, e.g. 0 -300)\n```\n\n### Navigation\n\n```bash\nplaywright-cli go-back --tab=<id> # history.back()\nplaywright-cli go-forward --tab=<id> # history.forward()\nplaywright-cli reload --tab=<id> # Reload page\n```\n\n### Teleport\n\n```bash\nplaywright-cli teleport --tab=<id> --start=<regex> --return=<regex> [--timeout=<s>]\nplaywright-cli teleport --list # List available follower runtimes\nplaywright-cli teleport --off --tab=<id> # Cancel a teleport on this tab\nplaywright-cli open <url> --teleport-start=<regex> --teleport-return=<regex>\nplaywright-cli goto --tab=<id> <url> --teleport-start=<regex> --teleport-return=<regex>\n```\n\nTeleport is for leader/follower tray auth handoffs. Scoped to a specific tab — only commands targeting the teleporting tab are blocked; other tabs remain operational.\n\n`--runtime` is optional: without it, teleport picks the most recently active eligible follower (preferring standalone floats). Only followers that can actually serve cookies are eligible — cherry hosts, exec-only CLI followers, and any target advertising `network: false` are excluded, so a teleport fails fast instead of \"succeeding\" with zero cookies.\n\n### Screenshots\n\n```bash\nplaywright-cli screenshot --tab=<id> # Save to $TMPDIR/screenshot-<ts>.png\nplaywright-cli screenshot --tab=<id> --filename=page.png # Save to custom path\nplaywright-cli screenshot --tab=<id> e5 # Clip to an element (positional = MAIN-FRAME ref, not a path)\nplaywright-cli screenshot --tab=<id> --fullPage # Full scrollable page (alias: --full-page)\nplaywright-cli screenshot --tab=<id> --max-width=800 # Downscale to a max width (png output only)\nplaywright-cli screenshot --tab=<id> --type=jpeg # png (default) / jpeg / webp; inferred from --filename extension\nplaywright-cli screenshot --tab=<id> --hires # Capture in device pixels (honors device pixel ratio)\n```\n\nViewport screenshots (default, `--max-width`, `--hires`) honor the tab's current scroll position. `--fullPage` captures from the document origin.\n\nAn element screenshot (`screenshot e5`) returns **that element's crop or fails**\n(exit 1) — typically because the snapshot went stale after a navigation or\nlayout change. Re-run `snapshot` and retry with a fresh ref; it never silently\nsubstitutes a full-viewport frame.\n\nA `resize` sticks to its tab: the viewport override is re-applied automatically\nwhenever the tab is re-attached, so another driver switching tabs cannot reset\nit.\n\nAll playwright-cli commands share one browser, and locking is **per tab**:\ncommands on different tabs run in parallel; commands on the same tab serialize.\nA slow or hung navigation stalls only its own tab. A few genuinely global\noperations (`tab-select`, `--foreground`, and the attach that points the bridge\nat a tab) still take a bridge-wide lock, but they are short and never wrap a\npage wait. When callers queue up, commands may emit a\n`note: browser bridge contended — ...` line on stderr with the total lock wait\nand queue depth; the note says whether the wait was on **this tab** or\n**bridge-wide**. Either way it is back-off guidance — stagger callers or give\neach one its own tab, never re-run the command; it already ran, it was just\nslow to get the lock.\n\n**Giving up cancels the command.** When your `bash` call stops waiting — the\nturn is cancelled, `timeout` fires, or someone runs `kill <pid>` — the\nplaywright-cli command is cancelled too instead of running on for a caller that\nis gone: a queued command drops out of the tab's queue without taking the lock,\na `goto` waiting on a load event and a `waitForSelector` poll stop at once, and\na multi-step command stops before its next CDP round trip. Such a command exits\n**130** with a stderr line naming the step it stopped at, never 0.\n\nTwo consequences for you:\n\n- A cancelled command may have **half-landed**. The CDP request already on the\n wire cannot be recalled, so keys may be typed or a click delivered before the\n stop. `snapshot` the tab and continue from what you see rather than blindly\n re-running — same rule as a mid-command session reset.\n- `background_after` alone does **not** cancel; it detaches, and the command\n keeps running as a background job (that is what makes a long `goto` usable).\n Use `timeout` or `kill <pid>` when you actually want it stopped.\n\n**Stale session.** The bridge re-attaches automatically after a Chrome reset;\nthere is no need to reload the tab or open a new one. Two error shapes:\n\n- `Session with given id not found` — the bridge already re-attached; re-run\n the command.\n- `… was reset mid-command … the outcome is unknown` — part of the command may\n have landed (keys typed, a click delivered) before the session died. Do NOT\n blindly re-run it: `snapshot` the tab first and continue from what you see.\n\n### Save As\n\n```bash\nplaywright-cli pdf --tab=<id> [--filename=path] # Save page as PDF (not available in extension mode)\n```\n\n### Viewing pages and screenshots yourself\n\nThe browser displays things to the human; `open --view` is what lets _you_ see them. **But viewing screenshots is a last resort for the cone — every image you load eats a large chunk of the context window** (a single 1280×800 PNG can run 1500+ tokens, full-page screenshots much more). Reach for cheaper signals first:\n\n1. **`playwright-cli snapshot --tab=<id>`** — text accessibility tree. Use this first; it answers \"what's on the page\" for almost all verification tasks at a tiny fraction of the token cost. On a big page, `find --tab=<id> <text>` greps the snapshot and returns just the matching lines with context, and `snapshot --depth=<n>` caps the tree — both much cheaper than the full tree.\n2. **`eval` against the DOM** — when you need a specific value (`document.title`, an attribute, computed style), `eval` it. Don't screenshot for facts you can extract.\n3. **Delegate visual inspection to a scoop.** If the cone genuinely needs vision (layout regression, render fidelity, \"does this look right\"), spawn a scoop to take and view the screenshot — the scoop's context absorbs the tokens, and you receive its summary back. The cone's window stays clean for orchestration.\n4. **`open --view` in the cone** — only when the cone itself must see pixels for its current decision and steps 1–3 won't do.\n\n**What you CAN see:**\n\n- `open --view <path>` — reads an image from the VFS and returns it. Works with PNG, JPEG, GIF, WebP, SVG.\n- `playwright-cli screenshot --tab=<id>` + `open --view <path>` — screenshot a tab, then view it.\n- `screencapture --view screenshot.png` — capture the user's screen via browser screen sharing.\n- `screencapture --video -V 10 clip.webm` — record a timed screen/window/tab clip (WebM).\n- `playwright-cli snapshot --tab=<id>` — accessibility tree (text). Use to verify content without vision.\n\n**What only the human sees:**\n\n- `serve <dir>` — opens an app directory in a browser tab (read-only preview).\n- Preview URLs survive authenticated tray roves without changing their original directory, entry file, or bridge permissions. A handoff can briefly return `503`; retry the same pending rove rather than minting another target. Bridge tabs reconnect automatically (their connection IDs may change). Revoked previews stay revoked, and persistent snapshots keep their original expiry rather than receiving a fresh TTL.\n- Persistent uploads retry safely only with the same file bytes and MIME type. Body/R2-write waits time out after 30 seconds. Unknown writes retain bounded cleanup bookkeeping (up to eight unresolved uploads per preview); expiry/revoke never makes such a preview serve again, even if a delayed write finishes later.\n- `serve --ttl 30d <dir>` — uploads an immutable snapshot that works without the leader. TTL units are whole `m`, `h`, `d`, or `w`, capped at 30 days. It implies `--no-bridge`, conflicts with `--bridge`/`--max-tabs`, and is limited to 1,000 files, 25 MiB per file, and 50 MiB total. Use `serve --list` for mode/expiry and `serve --stop <token>` to revoke it early.\n- **`serve` limits — plan for them:** a live-preview file over 25 MiB is served only through Range requests (a plain GET answers 413; media elements use ranges, so no splitting is needed), and `--ttl` files must be ≤ 25 MiB. A tray holds at most **10 `--ttl` snapshots** (uploads in progress count); live previews have no quota. A live preview is deleted once the leader has been disconnected for 5 minutes; a snapshot lasts until its TTL. Each `serve` prints `Preview token: …`; `serve --stop <token-or-url>` snapshots you no longer need, and on `Snapshot limit reached` run `serve --list` and stop old ones.\n- `serve --bridge <dir>` — opens a **driveable** preview whose visitors auto-connect as live synthetic-CDP targets you can navigate/click/evaluate/screenshot via playwright. **Security: opt-in only; cross-subdomain cookie risk accepted (host-only cookies isolated; `Domain=.sliccy.now` cookies readable across previews).** Flags: `--max-tabs <N>` (default 20), `--quiet` (suppress the first-visit announcement), `--no-bridge` (force read-only), `--stop <token>` (revoke + delete webhook). Use `serve --logs [<token>] [--lines <N>]` to inspect leader-memory-only connect/disconnect records without emitting a lick or waking the cone. `serve --truncate [<token>]` clears matching records **and re-arms the first-visit announcement**, so the next visit announces once. Visitor page API: `window.slicc.emit(name, detail?)` (fires webhook lick on cone), `window.slicc.on(name, cb)` (subscribes to CustomEvents you dispatch).\n- **Media in `serve` previews plays through Range requests:** live previews answer `Range` in ≤ 8 MiB windows, so `<video>`/`<audio>` larger than 25 MiB play and seek without splitting; only a plain full-file GET of such a file gets 413. `--ttl` snapshots support Range too (their files stay ≤ 25 MiB).\n- `open <path>` (no flags) — opens a file in a browser tab.\n- `imgcat <path>` — displays an image in the terminal preview.\n\n**Workflow to verify a page (when vision is actually required):**\n\n1. `serve /workspace/app` — open the app (the human sees it).\n2. `playwright-cli tab-list` — find the tab by URL, note the targetId.\n3. `playwright-cli snapshot --tab=<id>` — required before screenshot, and often answers your question on its own.\n4. `playwright-cli screenshot --tab=<id> --filename=\"$TMPDIR/shot.png\"` — consider `--max-width` to keep the file small.\n5. `open --view \"$TMPDIR/shot.png\"` — now you can see it. Strongly prefer doing this from a scoop, not the cone.\n\n**Don't:**\n\n- Default to screenshots when a snapshot would do.\n- `read_file` on a PNG or base64-encode to view images.\n- `imgcat` or `cat` on screenshots expecting to see them.\n- Open a screenshot then screenshot that tab.\n- Use `eval` to check the active tab — use `tab-list`.\n\n### Tab Management\n\n```bash\nplaywright-cli tab-list # List tabs with targetIds + (active) marker\nplaywright-cli tab-new [url] # New tab, returns targetId\nplaywright-cli tab-close --tab=<id> # Close specific tab\nplaywright-cli tab-select <index> # Select (bring to front) tab by 1-based index\n```\n\n### Cookies\n\n```bash\nplaywright-cli cookie-list --tab=<id> [--domain=<d>] [--path=<p>] # List cookies (filter by domain/path)\nplaywright-cli cookie-get --tab=<id> <name> # Get cookie\nplaywright-cli cookie-set --tab=<id> <name> <value> [--sameSite=Strict|Lax|None] [other flags] # Set cookie\nplaywright-cli cookie-delete --tab=<id> <name> [--domain= --path=] # Delete cookie\nplaywright-cli cookie-clear --tab=<id> # Clear all cookies\n```\n\n### Storage\n\n```bash\nplaywright-cli localstorage-list --tab=<id>\nplaywright-cli localstorage-get --tab=<id> <key>\nplaywright-cli localstorage-set --tab=<id> <key> <value>\nplaywright-cli localstorage-delete --tab=<id> <key>\nplaywright-cli localstorage-clear --tab=<id>\n# Same pattern for sessionstorage-*\nplaywright-cli state-save --tab=<id> [filename|--filename=path] # Save cookies + localStorage to JSON\nplaywright-cli state-load --tab=<id> <filename> # Restore from state file\n```\n\n### Network\n\n```bash\nplaywright-cli console --tab=<id> [min-level] [--clear] # List console messages (debug/log/info/warning/error)\nplaywright-cli requests --tab=<id> [--static] [--filter=<regex>] [--clear] # List network requests\nplaywright-cli request --tab=<id> <index> [--filename=path] # Full request details\nplaywright-cli request-headers --tab=<id> <index> # Request headers only\nplaywright-cli request-body --tab=<id> <index> # Request body only\nplaywright-cli response-headers --tab=<id> <index> # Response headers only\nplaywright-cli response-body --tab=<id> <index> [--filename=path] # Response body (--filename saves the exact bytes)\nplaywright-cli network-state-set --tab=<id> <online|offline> # Toggle network state\nplaywright-cli route --tab=<id> <pattern> [--status=N] [--body=text] [--content-type=type] [--header=name:value]\nplaywright-cli route-list --tab=<id> # List active routes\nplaywright-cli unroute --tab=<id> [pattern] # Remove routes (all if no pattern)\n```\n\n### DevTools\n\n```bash\nplaywright-cli generate-locator --tab=<id> <ref> # Generate Playwright locator string for element\nplaywright-cli highlight --tab=<id> <ref> [--style=<css>] # Highlight element with visual overlay\nplaywright-cli highlight --tab=<id> --hide [ref] # Remove highlight (all if no ref)\n```\n\n### HAR Recording\n\n```bash\nplaywright-cli record [url] [--filter=<js-expr>] # Open tab with network recording\nplaywright-cli stop-recording <recordingId> # Stop and save HAR\n```\n\n## Multi-Agent Tab Behavior\n\n**All agents (cone + scoops) share the same tab namespace.** There is no tab isolation.\n\n- `tab-list` shows **every** tab from every agent — yours, the cone's, other scoops'. The list can be noisy.\n- Any agent can `eval`, `snapshot`, or `close` any tab — there are no ownership checks.\n- Tab counts fluctuate as other agents open and close tabs concurrently.\n\n**Best practices for scoops:**\n\n1. **Track your own tab IDs.** When you open a tab, capture the targetId and store it. Don't rely on `tab-list` to find your tabs later — other agents' tabs will be mixed in.\n\n ```bash\n # Open and capture the ID\n playwright-cli tab-new https://example.com\n # Output: Opened https://example.com in new tab [targetId: ABC123...]\n # Use ABC123 for all subsequent commands on this tab\n ```\n\n2. **NEVER close tabs you didn't open.** Tabs you don't recognize belong to the **user** or other agents. User tabs are off-limits unless the user explicitly asks you to close them. Only close tabs whose targetId you captured from your own `tab-new` / `open` calls.\n\n3. **Handle \"tab not found\" gracefully.** Another agent might close a tab between your `tab-list` and your command. If you get `Error: No tab with id`, the tab is gone — move on.\n\n4. **Don't depend on tab count or ordering.** Other agents are opening/closing tabs concurrently. Use targetIds, not positional logic.\n\n5. **Clean up when done.** Close all tabs you opened before finishing. Include this in every scoop brief:\n _\"Close each tab with `playwright-cli tab-close --tab=<id>` when done.\"_\n\n## Tips\n\n- **Refs change after every interaction** — always re-snapshot before clicking or filling.\n- `open` and `tab-new` open tabs in the **background** by default. Capture the targetId from the output. To open in the **foreground** add `--foreground`/`--fg`; to raise an **already-open** tab use `tab-select <index>`.\n- After `click`, `fill`, `goto`, `go-back`, `go-forward`, `reload`, `select`, `check`, `uncheck`, `drag`, or `dialog-*`, take a fresh `snapshot --tab=<id>` before using refs again.\n- Unexpected JavaScript dialogs are auto-dismissed on attached pages.\n- Use `eval --tab=<id>` for DOM operations not covered by built-in commands; save results with `--filename=path`.\n- The SLICC app tab and Chrome internal UI tabs are automatically excluded from `tab-list`.\n- `fill` clears and types into regular inputs, textareas, and `contenteditable` elements. Use `--submit` to press Enter after. If the text or an `eval` expression starts with `-`, put `--` before it so it is not parsed as a flag.\n- Negative numbers for `mousewheel` / `mousemove` are positionals (no `--` needed): `mousewheel --tab=<id> 0 -300`.\n- `drop --path=<vfs-path>` injects the file as **raw bytes**; a UTF-8 text round-trip would replace every byte >= 0x80 with `EF BF BD`, so mp4/zip/jpg drops stay byte-exact.\n- Screenshots default to `$TMPDIR/screenshot-<timestamp>.png` — your own scratch directory, not the shared root. Use `--filename=path` to save elsewhere —\n `screenshot <path>` is an error, because that slot is the element ref.\n- Unsupported flags and extra positionals are rejected per subcommand, so a probe that exits 0 means\n the flag was honoured — nothing is silently dropped.\n- Use `keydown`/`keyup` for holding modifier keys (e.g. Shift+click: `keydown Shift`, `click`, `keyup Shift`).\n- Use `requests` + `response-body` to inspect XHR/fetch responses; `route` to mock or block them.\n- **Calling the app's own backend? Use `curlwright`, not `eval-file`.** It is curl's flags run by a `fetch()` inside the tab, so cookies, origin and session come along: `curlwright -s -X POST https://app.example.com/api/items -H 'X-CSRF-Token: abc' -d '{\"name\":\"x\"}' --tab=<id>`. `-o <file>` writes a **byte-exact** body, which `eval-file` cannot do at all — that is the only way to pull a binary response out of a page. Without `--tab` it uses the tab already on that origin. `curlwright --help` for the flag list. It cancels the same way playwright-cli does — giving up exits **130** and writes no `-o` file for a response nobody is reading — but the in-page `fetch` is bounded by `-m`, not by the cancellation, so a cancelled request may still have reached the server.\n- `state-save` / `state-load` persist auth state (cookies + localStorage) across sessions.\n- Every command is logged to `session.md` (auto-snapshots and screenshot copies alongside) under `/.playwright/` in the cone, or your own `$TMPDIR/.playwright/` in a scoop. `state-save` with no filename writes `storage-state.json` there too.\n- `pdf` saves a print-layout PDF; not available in extension mode.\n- `upload` injects VFS files as **raw bytes** (a UTF-8 text round-trip would replace every byte ≥ 0x80 with `EF BF BD`). A leading `eN` / `fNeN` token is a snapshot ref, never a filename — run `snapshot` first; without a ref, focus the file input first. To upload a file named `e2`, pass `./e2` or an absolute path.\n\n## Low-level CDP escape hatch\n\nFor raw Chrome DevTools Protocol calls that `playwright-cli` doesn't wrap, send JSON-RPC directly over WebSocket with `websocat`:\n\n```bash\n# Discover the page's debug socket URL via the CDP HTTP endpoint\ncurl -s http://127.0.0.1:9222/json | jq -r '.[0].webSocketDebuggerUrl'\n\n# Send a single CDP method, receive the response, exit\necho 'Page.navigate {\"url\":\"https://example.com\"}' \\\n | websocat -1 --jsonrpc --jsonrpc-omit-jsonrpc ws://127.0.0.1:9222/devtools/page/<id>\n```\n\nRun `websocat --help` for the full flag list. Use this only when `playwright-cli` has no wrapper for the CDP method you need.\n";export{e as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./py-realm-worker-BaO89JAR.js";var t=e(((e,t)=>{t.exports={}}));export default t();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/oauth-service-
|
|
2
|
-
import{n as e}from"./rolldown-runtime-Dd_uD5pT.js";import{a as t,i as n,o as r}from"./core-B3ULTK7V.js";import{t as i}from"./preload-helper-Czpn1I53.js";import{t as a}from"./logger-DDBAeTLF.js";import"./proxied-fetch-BlIpeDaL.js";import{s as o,t as s}from"./api-endpoint-DuEQRZ4g.js";import{n as c}from"./float-topology-BvkgNLoi.js";import"./secret-topology-DpJD8DzV.js";import{n as l}from"./secrets-bridge-client-CiA-2ZIU.js";import{n as ee,o as te,t as u}from"./providers-BvApGkM9.js";import{n as ne,t as re}from"./panel-rpc-D3EPRnmw.js";import{i as ie,r as ae,t as oe}from"./bedrock-camp-compat-BeCSBUrZ.js";import{r as se,t as ce}from"./bedrock-camp-extra-models-WTCIjj5a.js";import{o as d}from"./claude-model-version-DZmbk9E2.js";var le=`slicc_oauth_extra_domains`;function f(e){try{let t=e.getItem(le);if(!t)return{};let n=JSON.parse(t);if(typeof n!=`object`||!n||Array.isArray(n))return{};let r={};for(let[e,t]of Object.entries(n)){if(typeof e!=`string`||!Array.isArray(t))continue;let n=t.filter(e=>typeof e==`string`&&e.length>0);n.length>0&&(r[e]=n)}return r}catch{return{}}}function ue(e,t){try{e.setItem(le,JSON.stringify(t))}catch(e){throw Error(`Failed to persist OAuth extras (localStorage quota exceeded?): ${e instanceof Error?e.message:String(e)}`)}}function de(e,t){let n={input:0,output:0,cacheRead:0,cacheWrite:0},r=d(e);if(!r)return n;let i;for(let e of t.values()){let t=d(e.id);!t||t.family!==r.family||(!i||t.major>i.major||t.major===i.major&&t.minor>i.minor)&&(i={major:t.major,minor:t.minor,cost:e.cost})}return i?.cost??n}a(`model-policy`);var fe=`/etc/models`;function pe(){return{sections:{}}}function p(e,t,n){return e.providerId===t?e.modelId===`*`||e.modelId===n:!1}function me(e,t){return e.sections[t]??[]}function he(e,t,n,r){return me(e,t).some(e=>e.deny&&p(e,n,r))}function ge(e,t,n,r){let i=me(e,t);return i.some(e=>e.deny&&p(e,n,r))?!1:n===t||i.some(e=>!e.deny&&p(e,n,r))}var _e=pe();function ve(){return _e}function ye(e,t,n){return`add \`${t}:${n}\` (or \`${t}:*\`) under \`[${e}]\` in ${fe}`}var be=e({ACCOUNTS_KEY:()=>g,__test__:()=>Te,addAccount:()=>P,applyProviderDefaults:()=>ke,clearAllSettings:()=>ct,clearApiKey:()=>rt,clearBaseUrl:()=>ot,clearSelectedProvider:()=>et,ensureOAuthMaskReplica:()=>Ge,exportProviders:()=>st,getAccounts:()=>A,getAllAvailableModels:()=>k,getAllExtraOAuthDomains:()=>Pe,getAlternativeModelProviders:()=>Me,getApiKey:()=>tt,getApiKeyForProvider:()=>V,getApiVersionForProvider:()=>Xe,getAvailableProviders:()=>x,getBaseUrl:()=>it,getBaseUrlForProvider:()=>H,getDeploymentForProvider:()=>Ye,getExtraOAuthDomains:()=>j,getModelCatalogProviderIds:()=>ut,getOAuthAccountInfo:()=>De,getProviderConfig:()=>C,getProviderModels:()=>T,getRawApiKeyForProvider:()=>B,getSelectedModelId:()=>U,getSelectedProvider:()=>W,isModelHiddenFromPicker:()=>D,isUsableOAuthMaskReplica:()=>R,logoutOAuthAccount:()=>I,maskOAuthTokenWithRetry:()=>L,migrateLegacyAuthOnlySelection:()=>b,modelRunsOnProvider:()=>mt,persistOAuthMaskViaServiceWorker:()=>z,policyVisible:()=>O,providerOffersLlmModels:()=>S,removeAccount:()=>F,resolveCurrentModel:()=>J,resolveModelById:()=>q,resolveModelByShorthand:()=>ht,resolveModelIdForScoop:()=>Ct,resolveModelSelectionForScoop:()=>Q,saveOAuthAccount:()=>qe,setApiKey:()=>nt,setBaseUrl:()=>at,setExtraOAuthDomains:()=>M,setExtraOAuthDomainsAsync:()=>Ne,setSelectedModelId:()=>Ze,setSelectedProvider:()=>$e}),m=n,h=t,g=`slicc_accounts`,_=`selected-model`,xe=[`slicc_provider`,`slicc_api_key`,`slicc_base_url`,`anthropic_api_key`,`api_provider`,`azure_resource`,`bedrock_region`],Se=new Set([`github`]);function v(e){let t;try{t=r()}catch{return!1}return t.includes(e)&&!te(e)}var y=!1;function Ce(){if(!y){y=!0;for(let e of xe)try{localStorage.removeItem(e)}catch{}b()}}function b(){try{let e=localStorage.getItem(_);if(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t);(Se.has(n)||v(n))&&localStorage.removeItem(_)}}}catch{}try{let e=localStorage.getItem(g);if(!e)return;let t=JSON.parse(e);if(!Array.isArray(t))return;let n=t.filter(e=>!(typeof e==`object`&&e&&typeof e.providerId==`string`&&v(e.providerId)));n.length!==t.length&&localStorage.setItem(g,JSON.stringify(n))}catch{}}function we(){y=!1}var Te={_resetLegacyCleanup:we};function x(){let e=r().filter(te),t=ee();return[...new Set([...e,...t])].filter(e=>!u(e)?.hidden)}function S(e){try{if(T(e).length>0)return!0}catch{}try{if(t(e).length>0)return!0}catch{}let n=u(e);return!!(n?.modelOverrides&&Object.keys(n.modelOverrides).length>0)}function Ee(e,n){try{if(T(e).some(e=>e.id===n))return!0}catch{}try{if(t(e).some(e=>e.id===n))return!0}catch{}let r=u(e);return!!(r?.modelOverrides&&Object.hasOwn(r.modelOverrides,n))}function C(e){return u(e)||{id:e,name:e.split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `),description:`${e} provider`,requiresApiKey:!0,requiresBaseUrl:!1}}function w(e,t){t.context_window!==void 0&&(e.contextWindow=t.context_window),t.max_tokens!==void 0&&(e.maxTokens=t.max_tokens),t.reasoning!==void 0&&(e.reasoning=t.reasoning),t.input!==void 0&&(e.input=t.input),t.cost!==void 0&&(e.cost=t.cost,e.inputCost=t.cost.input,e.outputCost=t.cost.output,e.cacheReadCost=t.cost.cacheRead,e.cacheWriteCost=t.cost.cacheWrite),t.compat!==void 0&&(e.compat={...e.compat??{},...t.compat}),t.thinkingLevelMap!==void 0&&(e.thinkingLevelMap={...e.thinkingLevelMap??{},...t.thinkingLevelMap})}function T(e){try{if(e===`bedrock-camp`){let e=oe(H(`bedrock-camp`));return se(h(`amazon-bedrock`),ce).filter(t=>ie(t,e)).map(e=>({...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`,reasoning:e.reasoning===!0&&ae(e)}))}let t=C(e);if(t.getModelIds){let n;try{n=t.getModelIds()}catch(t){return E.error(`Provider getModelIds callback failed`,{providerId:e,error:t instanceof Error?t.message:String(t)}),[]}let i=new Map;for(let e of r())try{for(let t of h(e))i.set(t.id,t)}catch{}return n.map(n=>{let r=n.api===`openai`?`openai`:`anthropic`,a=`${e}-${r}`,o=i.get(n.id),s;if(o)s={...o,api:a,provider:e};else if(s=G(e,n.id,``,a),n.name&&(s.name=n.name),r===`anthropic`){let e=de(n.id,i);s.cost=e,s.inputCost=e.input,s.outputCost=e.output,s.cacheReadCost=e.cacheRead,s.cacheWriteCost=e.cacheWrite}let c=t.modelOverrides?.[n.id];return c&&w(s,c),w(s,n),s})}if(t.isOAuth){let n=h(`anthropic`),r=`${e}-anthropic`;return n.map(n=>{let i={...n,api:r,provider:e},a=t.modelOverrides?.[n.id];return a&&w(i,a),i})}return h(e===`azure-ai-foundry`?`anthropic`:e)}catch(t){return E.error(`Failed to load models`,{providerId:e,error:t instanceof Error?t.message:String(t)}),[]}}function De(e){let t=A().find(t=>t.providerId===e);if(!t?.accessToken)return null;let n=!!t.tokenExpiresAt&&Date.now()>t.tokenExpiresAt-6e4;return{token:t.accessToken,maskedValue:t.maskedValue,expiresAt:t.tokenExpiresAt,userName:t.userName,userAvatar:t.userAvatar,scopes:t.scopes,expired:n}}var Oe=Object.assign({})[`/packages/webapp/providers.json`]??[],E=a(`provider-settings`);function ke(e=Oe){if(e.length===0||A().length>0)return;let t=new Set(x());for(let n of e)if(!(!n.providerId||!n.apiKey)){if(!t.has(n.providerId)){E.warn(`Unknown provider "${n.providerId}" in providers.json — skipping`);continue}P(n.providerId,n.apiKey,n.baseUrl)}let n=e.find(e=>e.providerId&&e.apiKey&&t.has(e.providerId));n?.model&&!localStorage.getItem(_)&&localStorage.setItem(_,`${n.providerId}:${n.model}`)}var Ae=[/haiku/i];function D(e){return Ae.some(t=>t.test(e))}function je(e){return e.filter(e=>!D(e.id))}function O(e,t){let n=$();if(n===null)return e;let r=ve();return e.filter(e=>!he(r,n,t,e.id))}function k(){let e=A();if(e.length===0)return[];let t=new Map;for(let n of e){if(t.has(n.providerId))continue;let e=C(n.providerId);if(e.hidden||v(n.providerId))continue;let r=O(je(T(n.providerId)),n.providerId);if(r.length===0)continue;let i={providerId:n.providerId,providerName:e.name,models:r};t.set(n.providerId,i)}return[...t.values()]}function Me(e){return k().map(e=>e.providerId).filter(t=>t!==e)}function A(){Ce();let e=localStorage.getItem(g);if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.providerId==`string`&&typeof e.apiKey==`string`):[]}catch{return[]}}function j(e){return f(localStorage)[e]??[]}function M(e,t){let n=f(localStorage),r=t.map(e=>e.trim()).filter(e=>e.length>0);r.length===0?delete n[e]:n[e]=r,ue(localStorage,n)}async function Ne(e,t){if(ne()){M(e,t);return}let n=re();if(!n)throw Error(`setExtraOAuthDomainsAsync: no DOM and no panel-rpc client — cannot persist to page localStorage`);let{storeAfter:r}=await n.call(`oauth-extras-set`,{providerId:e,domains:t});try{ue(localStorage,r)}catch(t){E.warn(`worker-shim mirror failed after successful page write — reload to refresh`,{providerId:e,error:t instanceof Error?t.message:String(t)})}}function Pe(){return f(localStorage)}function Fe(e){localStorage.setItem(g,JSON.stringify(e))}async function N(e){if(ne()){Fe(e);return}let t=re();if(!t)throw Error(`saveAccountsAsync: no DOM and no panel-rpc client — cannot persist to page localStorage`);let n=JSON.stringify(e),{storedJson:r}=await t.call(`save-oauth-accounts`,{accountsJson:n});try{localStorage.setItem(g,r)}catch(e){E.warn(`worker-shim mirror failed after successful page write — reload to refresh`,{error:e instanceof Error?e.message:String(e)})}}function P(e,t,n,r,i){let a=A().filter(t=>t.providerId!==e),o={providerId:e,apiKey:t};n&&(o.baseUrl=n),r&&(o.deployment=r),i&&(o.apiVersion=i),a.push(o),Fe(a)}async function Ie(e){let t=c();try{if(t===`extension-direct`){let t=await new Promise(t=>{chrome.runtime.sendMessage({type:`secrets.delete`,name:`oauth.${e}.token`},n=>{chrome.runtime.lastError&&E.error(`SW secrets.delete transport failed`,{providerId:e,error:chrome.runtime.lastError.message}),t(n??{})})});t.error&&E.error(`SW secrets.delete returned error`,{providerId:e,error:t.error})}else if(t===`extension-delegate`){let t=await l(`secrets.delete`,{name:`oauth.${e}.token`});t?.error&&E.error(`Bridge secrets.delete returned error`,{providerId:e,error:t.error})}else if(t!==`connect`){let t=await fetch(o(`/api/secrets/oauth/${e}`),{method:`DELETE`,headers:s()});!t.ok&&t.status!==404&&E.warn(`OAuth replica DELETE non-ok`,{providerId:e,status:t.status})}}catch(n){E.error(`OAuth replica removal failed`,{providerId:e,topology:t,error:n instanceof Error?n.message:String(n)})}}async function F(e){let t=A().find(t=>t.providerId===e),n=C(e);t&&n?.isOAuth&&await I(e),await Ie(e),await N(A().filter(t=>t.providerId!==e));let r=localStorage.getItem(_)??``,i=r.indexOf(`:`);i>0&&r.slice(0,i)===e&&localStorage.removeItem(_)}async function I(e){let t=A().find(t=>t.providerId===e);if(!t)return;let n=C(e);if(n?.isOAuth){if(n.onOAuthLogout)try{await n.onOAuthLogout()}catch(t){E.warn(`onOAuthLogout failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})}if(n.getOAuthLogoutUrl){let r=n.getOAuthLogoutUrl(t);if(r){let{openIdpLogoutUrl:t}=await i(async()=>{let{openIdpLogoutUrl:e}=await import(`./oauth-service-B8mWc7rY.js`);return{openIdpLogoutUrl:e}},__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]));await t(r).catch(t=>{E.warn(`IdP logout popup failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})})}}await N(A().map(t=>t.providerId===e?{providerId:t.providerId,apiKey:``,baseUrl:t.baseUrl,userName:t.userName,userAvatar:t.userAvatar,loggedOut:!0}:t)),await Ie(e)}}async function L(e,t={}){let n=Math.max(1,t.attempts??3),r=t.delayMs??150,i=t.sleep??(e=>new Promise(t=>setTimeout(t,e))),a;for(let t=0;t<n;t++){let o=await e();if(o.maskedValue)return{maskedValue:o.maskedValue};o.error&&(a=o.error),t<n-1&&await i(r)}return{lastError:a}}var Le=`access token rotated during mask write`;function Re(e,t,n,r){let i=r.find(t=>t.providerId===e);return i?.accessToken?i.accessToken===t?(i.maskedValue=n,{maskedValue:n}):(E.warn(`OAuth mask write raced with token rotation; discarding replica`,{providerId:e}),{error:Le}):{error:`account gone after mask write`}}function R(e,t){return!!e&&e!==t}async function z(e,t,n){let r={providerId:e.providerId,accessToken:e.accessToken,domains:e.domains.join(`,`)},{maskedValue:i,lastError:a}=await L(()=>t.sendMaskRequest(r),n);if(!R(i,e.accessToken)){let t=i?`mask replica equals the access token`:a??`no error reported (cold SW or empty reply)`;return E.error(`OAuth mask give-up: no masked value after retries`,{providerId:e.providerId,reason:t}),{error:t}}let o=t.getAccounts(),s=Re(e.providerId,e.accessToken,i,o);return s.maskedValue&&await t.saveAccounts(o),s}function ze(e){let t=C(e).oauthTokenDomains??[],n=j(e),r=new Set,i=[];for(let e of[...t,...n]){let t=e.toLowerCase();r.has(t)||(r.add(t),i.push(e))}return i}function Be(e){return new Promise(t=>{chrome.runtime.sendMessage({type:`secrets.mask-oauth-token`,...e},n=>{chrome.runtime.lastError&&E.error(`SW mask-oauth-token transport failed`,{providerId:e.providerId,error:chrome.runtime.lastError.message});let r=typeof n==`object`&&n?n:void 0;r?.error&&E.warn(`SW mask-oauth-token returned error`,{providerId:e.providerId,error:r.error}),t(r??{})})})}function Ve(e){return l(`secrets.mask-oauth-token`,e).then(t=>(t?.error&&E.warn(`Bridge mask-oauth-token returned error`,{providerId:e.providerId,error:t.error}),t??{})).catch(t=>(E.error(`Bridge mask-oauth-token transport failed`,{providerId:e.providerId,error:t instanceof Error?t.message:String(t)}),{}))}async function He(e,t,n){let r=o(`/api/secrets/oauth-update`),i=await fetch(r,{method:`POST`,headers:s({"Content-Type":`application/json`}),body:JSON.stringify({providerId:e,accessToken:t,domains:n})});if(!i.ok)return E.warn(`OAuth replica POST non-ok`,{providerId:e,status:i.status,url:r}),{error:`OAuth replica POST HTTP ${i.status}`};let a=await i.json();if(typeof a.maskedValue!=`string`)return E.warn(`OAuth replica POST ok but missing maskedValue`,{providerId:e,url:r}),{error:`OAuth replica POST ok but missing maskedValue`};if(!R(a.maskedValue,t))return E.error(`OAuth mask replica equals the access token; refusing to persist`,{providerId:e}),{error:`mask replica equals the access token`};let c=A(),l=Re(e,t,a.maskedValue,c);return l.maskedValue&&await N(c),l}async function Ue(e,t){let n=await We(e,t);if(n.error!==Le)return n;let r=A().find(t=>t.providerId===e)?.accessToken;return!r||r===t?n:We(e,r)}async function We(e,t){let n=ze(e);if(n.length===0)return{error:`no oauth token domains configured`};let r=c();try{return r===`extension-direct`?z({providerId:e,accessToken:t,domains:n},{sendMaskRequest:Be,getAccounts:A,saveAccounts:N}):r===`extension-delegate`?z({providerId:e,accessToken:t,domains:n},{sendMaskRequest:Ve,getAccounts:A,saveAccounts:N}):r===`connect`?{error:`no replica store in connect mode`}:await He(e,t,n)}catch(t){let n=t instanceof Error?t.message:String(t);return E.error(`OAuth replica sync failed`,{providerId:e,topology:r,error:n}),{error:n}}}async function Ge(e){let t=A().find(t=>t.providerId===e);return t?.accessToken?R(t.maskedValue,t.accessToken)?{maskedValue:t.maskedValue}:Ue(e,t.accessToken):{error:`no access token held`}}function Ke(e,t){if(e.accessToken)return`scopes`in e?e.scopes:t?.scopes}async function qe(e){let t=A().find(t=>t.providerId===e.providerId),n=A().filter(t=>t.providerId!==e.providerId);n.push({providerId:e.providerId,apiKey:``,accessToken:e.accessToken,refreshToken:e.refreshToken,tokenExpiresAt:e.tokenExpiresAt,userName:e.userName,userAvatar:e.userAvatar,baseUrl:e.baseUrl??t?.baseUrl,scopes:Ke(e,t)}),await N(n),await Ue(e.providerId,e.accessToken)}var Je=`local`;function B(e){let t=A().find(t=>t.providerId===e);return t&&(t.accessToken||t.apiKey)||null}function V(e){let t=A().find(t=>t.providerId===e);return t?t.accessToken||t.apiKey||(C(e).optionalApiKey?Je:null):null}function H(e){return A().find(t=>t.providerId===e)?.baseUrl??null}function Ye(e){return A().find(t=>t.providerId===e)?.deployment??null}function Xe(e){return A().find(t=>t.providerId===e)?.apiVersion??null}function U(){let e=localStorage.getItem(_)||``,t=e.indexOf(`:`);return t>=0?e.slice(t+1):e}function Ze(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t),r=new Set([...ee(),...x()]),i=n.includes(`.`);if(r.has(n)||!i){localStorage.setItem(_,e);return}}let n=W();localStorage.setItem(_,`${n}:${e}`)}function Qe(){return localStorage.getItem(_)||``}function W(){let e=Qe(),t=e.indexOf(`:`);if(t>0)return e.slice(0,t);let n=A(),r=U();if(r){let e=n.find(e=>S(e.providerId)&&Ee(e.providerId,r));if(e)return e.providerId}let i=n.find(e=>S(e.providerId));return i?i.providerId:n.length>0?n[0].providerId:`anthropic`}function $e(e){let t=U();localStorage.setItem(_,`${e}:${t}`)}function et(){let e=U();localStorage.setItem(_,e)}function tt(){return V(W())}function nt(e){let t=W();P(t,e,H(t)??void 0)}async function rt(){await F(W())}function it(){return H(W())}function at(e){let t=W(),n=B(t);n&&P(t,n,e||void 0)}function ot(){let e=W(),t=B(e);t&&P(e,t)}function st(){let e=A(),t=W(),n=U();return e.map(e=>{let r={providerId:e.providerId,apiKey:e.apiKey};return e.baseUrl&&(r.baseUrl=e.baseUrl),e.providerId===t&&n&&(r.model=n),r})}async function ct(){let e=A();await Promise.allSettled(e.map(e=>F(e.providerId))),localStorage.removeItem(g),localStorage.removeItem(_);for(let e of xe)localStorage.removeItem(e)}function lt(e){return/^(?:gpt[-.]?|o[0-9]|chatgpt)/i.test(e)?`openai`:`anthropic`}function G(e,t,n,r){return{id:t,name:t,provider:e,api:r??`${e}-${lt(t)}`,baseUrl:n??``,contextWindow:2e5,maxTokens:16384,input:[`text`,`image`],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},reasoning:!0}}function K(e,t){return t.isOAuth||e===`azure-ai-foundry`?`anthropic`:e===`bedrock-camp`?`amazon-bedrock`:e}function ut(){let e=new Set;for(let t of A())t.loggedOut||(e.add(t.providerId),e.add(K(t.providerId,C(t.providerId))));return[...e]}function dt(e,t,n){let r=T(e).find(e=>e.id===t);return r?n?{...r,baseUrl:n}:r:null}function ft(e,t,n,r){return n.isOAuth?T(t).find(e=>e.id===r)||{...e,api:`${t}-anthropic`,provider:t}:t===`bedrock-camp`?{...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`}:e}function pt(e,t,n,r,i){let a=dt(e,n,r);if(a)return a;if(t.isOAuth)return G(e,n,r);if(i)throw Error(`Model ${n} is not available from provider ${e}`);return J()}function q(e,t){if(!e)return J();let n=t??W(),r=H(n),i=C(n);if(t!==void 0){let t=dt(n,e,r);if(t)return t}try{let t=m(K(n,i),e);if(!t?.id)throw Error(`Model ${e} not found`);let a=ft(t,n,i,e);return r?{...a,baseUrl:r}:a}catch(a){return E.debug(`resolveModelById: pi-ai lookup miss, using provider fallback`,{providerId:n,modelId:e,error:a instanceof Error?a.message:String(a)}),pt(n,i,e,r,t!==void 0)}}function mt(e,t){return e.provider===t||e.provider===K(t,C(t))}function J(){let e=W(),t=U(),n=H(e),r=T(e),i=C(e),a=i.defaultModelId?r.find(e=>e.id.toLowerCase().includes(i.defaultModelId.toLowerCase()))?.id:void 0,o=t||a||r[0]?.id||`claude-sonnet-4-6`;try{let t=K(e,i),a=m(t,o);if(!a?.id)throw Error(`Model ${o} not found in ${t} registry`);let s=a;return i.isOAuth?s=r.find(e=>e.id===o)||{...s,api:`${e}-anthropic`,provider:e}:e===`bedrock-camp`&&(s={...s,api:`bedrock-camp-converse`,provider:`bedrock-camp`}),n&&(s={...s,baseUrl:n}),s}catch(t){E.debug(`resolveCurrentModel: pi-ai lookup miss, using provider fallback`,{providerId:e,effectiveModelId:o,error:t instanceof Error?t.message:String(t)});let a=r.find(e=>e.id===o);return a?n?{...a,baseUrl:n}:a:i.isOAuth?G(e,o,n):m(`anthropic`,`claude-sonnet-4-0`)}}function ht(e){let t=e.toLowerCase();if(!t)return null;let n=null;try{n=W()}catch{}if(n!==null){let e=Y(t,[n]);if(e!==null)return e}return Y(t,A().map(e=>e.providerId))}function Y(e,t){let n=null,r=-1;for(let i of t)for(let t of T(i)){let i=t.id.toLowerCase(),a=(t.name??``).toLowerCase();if(!i.includes(e)&&!a.includes(e))continue;let o=t.contextWindow??0;(o>r||o===r&&wt(t.id,n??``)>0)&&(r=o,n=t.id)}return n}function X(){let e=[];try{for(let t of A())e.includes(t.providerId)||e.push(t.providerId)}catch{}try{let t=W();e.includes(t)||e.push(t)}catch{}return e}function gt(e){let t=e.indexOf(`:`);if(t<=0||t===e.length-1)return null;let n=e.slice(0,t),r=e.slice(t+1),i=X().includes(n);if(i)return{providerId:n,modelId:r,configured:i};let a=!1;try{a=x().includes(n)}catch{}return a?{providerId:n,modelId:r,configured:!1}:null}function Z(e,t,n=!0){let r=[e],i=n?Y(e.toLowerCase(),[t]):null;i!==null&&i!==e&&r.push(i);let a=[];try{a=T(t)}catch{}for(let n of r)if(!(a.length>0&&!a.some(e=>e.id===n)))try{if(q(n,t).id===n)return{modelId:n,providerId:t}}catch(r){E.debug(`selectModelFromProvider: candidate did not resolve`,{input:e,candidate:n,providerId:t,error:r instanceof Error?r.message:String(r)})}return null}function _t(e){return e.map(e=>`${e.providerId}:${e.modelId}`).join(`, `)}function Q(e){if(!e)return{ok:!1,error:`unknown model: (empty)`};let t=gt(e);if(t)return xt(e,t);let n=null;try{n=W()}catch{}if(n!==null){let t=Z(e,n);if(t)return bt(t)}return St(e,n)}function $(){try{return W()}catch{return null}}function vt(e){let t=$();return t===null||ge(ve(),t,e.providerId,e.modelId)}function yt(e){let t=$()??`<none>`;return{ok:!1,error:`model not allowed: ${`${e.providerId}:${e.modelId}`} is blocked by ${fe} while "${t}" is selected — ${ye(t,e.providerId,e.modelId)}`}}function bt(e){return vt(e)?{ok:!0,selection:e}:yt(e)}function xt(e,t){if(!t.configured){let n=X();return{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" is not configured${n.length>0?`; configured: ${n.join(`, `)}`:``})`}}let n=Z(t.modelId,t.providerId);return n?bt(n):{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" does not offer "${t.modelId}")`}}function St(e,t){let n=X().filter(e=>e!==t);for(let t of[`exact`,`shorthand`]){let r=[];for(let i of n){let n=t===`exact`?e:Y(e.toLowerCase(),[i]);if(n===null)continue;let a=Z(n,i,!1);a&&r.push(a)}let i=r.filter(vt);if(i.length===0&&r.length>0)return yt(r[0]);if(i.length===1)return{ok:!0,selection:i[0]};if(i.length>1)return{ok:!1,error:`ambiguous model: ${e} matches ${_t(i)} — qualify it as provider:model`}}return{ok:!1,error:`unknown model: ${e}`}}function Ct(e){let t=Q(e);return t.ok?t.selection.modelId:null}function wt(e,t){let n=e.match(/\d+/g)?.map(Number)??[],r=t.match(/\d+/g)?.map(Number)??[],i=Math.max(n.length,r.length);for(let e=0;e<i;e++){let t=(n[e]??0)-(r[e]??0);if(t!==0)return t}return 0}export{de as $,W as A,F as B,j as C,T as D,C as E,b as F,Q as G,q as H,mt as I,at as J,qe as K,z as L,R as M,I as N,B as O,L as P,$e as Q,O as R,Ye as S,De as T,ht as U,J as V,Ct as W,Ne as X,M as Y,Ze as Z,V as _,ke as a,it as b,ot as c,st as d,A as f,tt as g,Me as h,P as i,D as j,U as k,et as l,Pe as m,Te as n,ct as o,k as p,nt as q,be as r,rt as s,g as t,Ge as u,Xe as v,ut as w,H as x,x as y,S as z};
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/oauth-service-_PUd0RqU.js","assets/preload-helper-Czpn1I53.js","assets/proxied-fetch-DiXk_XGs.js","assets/rolldown-runtime-Dd_uD5pT.js","assets/base64-ELzoYfwP.js","assets/fetch-body-1l9I58Vk.js","assets/api-endpoint-DuEQRZ4g.js","assets/runtime-env-Dlaokqg1.js","assets/panel-rpc-D3EPRnmw.js","assets/permissions-surface-registry-CilCRwhh.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{n as e}from"./rolldown-runtime-Dd_uD5pT.js";import{a as t,i as n,o as r}from"./core-Be1yHH6v.js";import{t as i}from"./preload-helper-Czpn1I53.js";import{t as a}from"./logger-DDBAeTLF.js";import"./proxied-fetch-DiXk_XGs.js";import{s as o,t as s}from"./api-endpoint-DuEQRZ4g.js";import{n as c}from"./float-topology-B0_dG7En.js";import"./secret-topology-CZv6A9HW.js";import{n as l}from"./secrets-bridge-client-B_CG-HIg.js";import{n as ee,o as te,t as u}from"./providers-COWrD21n.js";import{n as ne,t as re}from"./panel-rpc-D3EPRnmw.js";import{i as ie,r as ae,t as oe}from"./bedrock-camp-compat-BeCSBUrZ.js";import{r as se,t as ce}from"./bedrock-camp-extra-models-WTCIjj5a.js";import{o as d}from"./claude-model-version-DZmbk9E2.js";var le=`slicc_oauth_extra_domains`;function f(e){try{let t=e.getItem(le);if(!t)return{};let n=JSON.parse(t);if(typeof n!=`object`||!n||Array.isArray(n))return{};let r={};for(let[e,t]of Object.entries(n)){if(typeof e!=`string`||!Array.isArray(t))continue;let n=t.filter(e=>typeof e==`string`&&e.length>0);n.length>0&&(r[e]=n)}return r}catch{return{}}}function ue(e,t){try{e.setItem(le,JSON.stringify(t))}catch(e){throw Error(`Failed to persist OAuth extras (localStorage quota exceeded?): ${e instanceof Error?e.message:String(e)}`)}}function de(e,t){let n={input:0,output:0,cacheRead:0,cacheWrite:0},r=d(e);if(!r)return n;let i;for(let e of t.values()){let t=d(e.id);!t||t.family!==r.family||(!i||t.major>i.major||t.major===i.major&&t.minor>i.minor)&&(i={major:t.major,minor:t.minor,cost:e.cost})}return i?.cost??n}a(`model-policy`);var fe=`/etc/models`;function pe(){return{sections:{}}}function p(e,t,n){return e.providerId===t?e.modelId===`*`||e.modelId===n:!1}function me(e,t){return e.sections[t]??[]}function he(e,t,n,r){return me(e,t).some(e=>e.deny&&p(e,n,r))}function ge(e,t,n,r){let i=me(e,t);return i.some(e=>e.deny&&p(e,n,r))?!1:n===t||i.some(e=>!e.deny&&p(e,n,r))}var _e=pe();function ve(){return _e}function ye(e,t,n){return`add \`${t}:${n}\` (or \`${t}:*\`) under \`[${e}]\` in ${fe}`}var be=e({ACCOUNTS_KEY:()=>g,__test__:()=>Te,addAccount:()=>P,applyProviderDefaults:()=>ke,clearAllSettings:()=>ct,clearApiKey:()=>rt,clearBaseUrl:()=>ot,clearSelectedProvider:()=>et,ensureOAuthMaskReplica:()=>Ge,exportProviders:()=>st,getAccounts:()=>A,getAllAvailableModels:()=>k,getAllExtraOAuthDomains:()=>Pe,getAlternativeModelProviders:()=>Me,getApiKey:()=>tt,getApiKeyForProvider:()=>V,getApiVersionForProvider:()=>Xe,getAvailableProviders:()=>x,getBaseUrl:()=>it,getBaseUrlForProvider:()=>H,getDeploymentForProvider:()=>Ye,getExtraOAuthDomains:()=>j,getModelCatalogProviderIds:()=>ut,getOAuthAccountInfo:()=>De,getProviderConfig:()=>C,getProviderModels:()=>T,getRawApiKeyForProvider:()=>B,getSelectedModelId:()=>U,getSelectedProvider:()=>W,isModelHiddenFromPicker:()=>D,isUsableOAuthMaskReplica:()=>R,logoutOAuthAccount:()=>I,maskOAuthTokenWithRetry:()=>L,migrateLegacyAuthOnlySelection:()=>b,modelRunsOnProvider:()=>mt,persistOAuthMaskViaServiceWorker:()=>z,policyVisible:()=>O,providerOffersLlmModels:()=>S,removeAccount:()=>F,resolveCurrentModel:()=>J,resolveModelById:()=>q,resolveModelByShorthand:()=>ht,resolveModelIdForScoop:()=>Ct,resolveModelSelectionForScoop:()=>Q,saveOAuthAccount:()=>qe,setApiKey:()=>nt,setBaseUrl:()=>at,setExtraOAuthDomains:()=>M,setExtraOAuthDomainsAsync:()=>Ne,setSelectedModelId:()=>Ze,setSelectedProvider:()=>$e}),m=n,h=t,g=`slicc_accounts`,_=`selected-model`,xe=[`slicc_provider`,`slicc_api_key`,`slicc_base_url`,`anthropic_api_key`,`api_provider`,`azure_resource`,`bedrock_region`],Se=new Set([`github`]);function v(e){let t;try{t=r()}catch{return!1}return t.includes(e)&&!te(e)}var y=!1;function Ce(){if(!y){y=!0;for(let e of xe)try{localStorage.removeItem(e)}catch{}b()}}function b(){try{let e=localStorage.getItem(_);if(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t);(Se.has(n)||v(n))&&localStorage.removeItem(_)}}}catch{}try{let e=localStorage.getItem(g);if(!e)return;let t=JSON.parse(e);if(!Array.isArray(t))return;let n=t.filter(e=>!(typeof e==`object`&&e&&typeof e.providerId==`string`&&v(e.providerId)));n.length!==t.length&&localStorage.setItem(g,JSON.stringify(n))}catch{}}function we(){y=!1}var Te={_resetLegacyCleanup:we};function x(){let e=r().filter(te),t=ee();return[...new Set([...e,...t])].filter(e=>!u(e)?.hidden)}function S(e){try{if(T(e).length>0)return!0}catch{}try{if(t(e).length>0)return!0}catch{}let n=u(e);return!!(n?.modelOverrides&&Object.keys(n.modelOverrides).length>0)}function Ee(e,n){try{if(T(e).some(e=>e.id===n))return!0}catch{}try{if(t(e).some(e=>e.id===n))return!0}catch{}let r=u(e);return!!(r?.modelOverrides&&Object.hasOwn(r.modelOverrides,n))}function C(e){return u(e)||{id:e,name:e.split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `),description:`${e} provider`,requiresApiKey:!0,requiresBaseUrl:!1}}function w(e,t){t.context_window!==void 0&&(e.contextWindow=t.context_window),t.max_tokens!==void 0&&(e.maxTokens=t.max_tokens),t.reasoning!==void 0&&(e.reasoning=t.reasoning),t.input!==void 0&&(e.input=t.input),t.cost!==void 0&&(e.cost=t.cost,e.inputCost=t.cost.input,e.outputCost=t.cost.output,e.cacheReadCost=t.cost.cacheRead,e.cacheWriteCost=t.cost.cacheWrite),t.compat!==void 0&&(e.compat={...e.compat??{},...t.compat}),t.thinkingLevelMap!==void 0&&(e.thinkingLevelMap={...e.thinkingLevelMap??{},...t.thinkingLevelMap})}function T(e){try{if(e===`bedrock-camp`){let e=oe(H(`bedrock-camp`));return se(h(`amazon-bedrock`),ce).filter(t=>ie(t,e)).map(e=>({...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`,reasoning:e.reasoning===!0&&ae(e)}))}let t=C(e);if(t.getModelIds){let n;try{n=t.getModelIds()}catch(t){return E.error(`Provider getModelIds callback failed`,{providerId:e,error:t instanceof Error?t.message:String(t)}),[]}let i=new Map;for(let e of r())try{for(let t of h(e))i.set(t.id,t)}catch{}return n.map(n=>{let r=n.api===`openai`?`openai`:`anthropic`,a=`${e}-${r}`,o=i.get(n.id),s;if(o)s={...o,api:a,provider:e};else if(s=G(e,n.id,``,a),n.name&&(s.name=n.name),r===`anthropic`){let e=de(n.id,i);s.cost=e,s.inputCost=e.input,s.outputCost=e.output,s.cacheReadCost=e.cacheRead,s.cacheWriteCost=e.cacheWrite}let c=t.modelOverrides?.[n.id];return c&&w(s,c),w(s,n),s})}if(t.isOAuth){let n=h(`anthropic`),r=`${e}-anthropic`;return n.map(n=>{let i={...n,api:r,provider:e},a=t.modelOverrides?.[n.id];return a&&w(i,a),i})}return h(e===`azure-ai-foundry`?`anthropic`:e)}catch(t){return E.error(`Failed to load models`,{providerId:e,error:t instanceof Error?t.message:String(t)}),[]}}function De(e){let t=A().find(t=>t.providerId===e);if(!t?.accessToken)return null;let n=!!t.tokenExpiresAt&&Date.now()>t.tokenExpiresAt-6e4;return{token:t.accessToken,maskedValue:t.maskedValue,expiresAt:t.tokenExpiresAt,userName:t.userName,userAvatar:t.userAvatar,scopes:t.scopes,expired:n}}var Oe=Object.assign({})[`/packages/webapp/providers.json`]??[],E=a(`provider-settings`);function ke(e=Oe){if(e.length===0||A().length>0)return;let t=new Set(x());for(let n of e)if(!(!n.providerId||!n.apiKey)){if(!t.has(n.providerId)){E.warn(`Unknown provider "${n.providerId}" in providers.json — skipping`);continue}P(n.providerId,n.apiKey,n.baseUrl)}let n=e.find(e=>e.providerId&&e.apiKey&&t.has(e.providerId));n?.model&&!localStorage.getItem(_)&&localStorage.setItem(_,`${n.providerId}:${n.model}`)}var Ae=[/haiku/i];function D(e){return Ae.some(t=>t.test(e))}function je(e){return e.filter(e=>!D(e.id))}function O(e,t){let n=$();if(n===null)return e;let r=ve();return e.filter(e=>!he(r,n,t,e.id))}function k(){let e=A();if(e.length===0)return[];let t=new Map;for(let n of e){if(t.has(n.providerId))continue;let e=C(n.providerId);if(e.hidden||v(n.providerId))continue;let r=O(je(T(n.providerId)),n.providerId);if(r.length===0)continue;let i={providerId:n.providerId,providerName:e.name,models:r};t.set(n.providerId,i)}return[...t.values()]}function Me(e){return k().map(e=>e.providerId).filter(t=>t!==e)}function A(){Ce();let e=localStorage.getItem(g);if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.providerId==`string`&&typeof e.apiKey==`string`):[]}catch{return[]}}function j(e){return f(localStorage)[e]??[]}function M(e,t){let n=f(localStorage),r=t.map(e=>e.trim()).filter(e=>e.length>0);r.length===0?delete n[e]:n[e]=r,ue(localStorage,n)}async function Ne(e,t){if(ne()){M(e,t);return}let n=re();if(!n)throw Error(`setExtraOAuthDomainsAsync: no DOM and no panel-rpc client — cannot persist to page localStorage`);let{storeAfter:r}=await n.call(`oauth-extras-set`,{providerId:e,domains:t});try{ue(localStorage,r)}catch(t){E.warn(`worker-shim mirror failed after successful page write — reload to refresh`,{providerId:e,error:t instanceof Error?t.message:String(t)})}}function Pe(){return f(localStorage)}function Fe(e){localStorage.setItem(g,JSON.stringify(e))}async function N(e){if(ne()){Fe(e);return}let t=re();if(!t)throw Error(`saveAccountsAsync: no DOM and no panel-rpc client — cannot persist to page localStorage`);let n=JSON.stringify(e),{storedJson:r}=await t.call(`save-oauth-accounts`,{accountsJson:n});try{localStorage.setItem(g,r)}catch(e){E.warn(`worker-shim mirror failed after successful page write — reload to refresh`,{error:e instanceof Error?e.message:String(e)})}}function P(e,t,n,r,i){let a=A().filter(t=>t.providerId!==e),o={providerId:e,apiKey:t};n&&(o.baseUrl=n),r&&(o.deployment=r),i&&(o.apiVersion=i),a.push(o),Fe(a)}async function Ie(e){let t=c();try{if(t===`extension-direct`){let t=await new Promise(t=>{chrome.runtime.sendMessage({type:`secrets.delete`,name:`oauth.${e}.token`},n=>{chrome.runtime.lastError&&E.error(`SW secrets.delete transport failed`,{providerId:e,error:chrome.runtime.lastError.message}),t(n??{})})});t.error&&E.error(`SW secrets.delete returned error`,{providerId:e,error:t.error})}else if(t===`extension-delegate`){let t=await l(`secrets.delete`,{name:`oauth.${e}.token`});t?.error&&E.error(`Bridge secrets.delete returned error`,{providerId:e,error:t.error})}else if(t!==`connect`){let t=await fetch(o(`/api/secrets/oauth/${e}`),{method:`DELETE`,headers:s()});!t.ok&&t.status!==404&&E.warn(`OAuth replica DELETE non-ok`,{providerId:e,status:t.status})}}catch(n){E.error(`OAuth replica removal failed`,{providerId:e,topology:t,error:n instanceof Error?n.message:String(n)})}}async function F(e){let t=A().find(t=>t.providerId===e),n=C(e);t&&n?.isOAuth&&await I(e),await Ie(e),await N(A().filter(t=>t.providerId!==e));let r=localStorage.getItem(_)??``,i=r.indexOf(`:`);i>0&&r.slice(0,i)===e&&localStorage.removeItem(_)}async function I(e){let t=A().find(t=>t.providerId===e);if(!t)return;let n=C(e);if(n?.isOAuth){if(n.onOAuthLogout)try{await n.onOAuthLogout()}catch(t){E.warn(`onOAuthLogout failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})}if(n.getOAuthLogoutUrl){let r=n.getOAuthLogoutUrl(t);if(r){let{openIdpLogoutUrl:t}=await i(async()=>{let{openIdpLogoutUrl:e}=await import(`./oauth-service-_PUd0RqU.js`);return{openIdpLogoutUrl:e}},__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]));await t(r).catch(t=>{E.warn(`IdP logout popup failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})})}}await N(A().map(t=>t.providerId===e?{providerId:t.providerId,apiKey:``,baseUrl:t.baseUrl,userName:t.userName,userAvatar:t.userAvatar,loggedOut:!0}:t)),await Ie(e)}}async function L(e,t={}){let n=Math.max(1,t.attempts??3),r=t.delayMs??150,i=t.sleep??(e=>new Promise(t=>setTimeout(t,e))),a;for(let t=0;t<n;t++){let o=await e();if(o.maskedValue)return{maskedValue:o.maskedValue};o.error&&(a=o.error),t<n-1&&await i(r)}return{lastError:a}}var Le=`access token rotated during mask write`;function Re(e,t,n,r){let i=r.find(t=>t.providerId===e);return i?.accessToken?i.accessToken===t?(i.maskedValue=n,{maskedValue:n}):(E.warn(`OAuth mask write raced with token rotation; discarding replica`,{providerId:e}),{error:Le}):{error:`account gone after mask write`}}function R(e,t){return!!e&&e!==t}async function z(e,t,n){let r={providerId:e.providerId,accessToken:e.accessToken,domains:e.domains.join(`,`)},{maskedValue:i,lastError:a}=await L(()=>t.sendMaskRequest(r),n);if(!R(i,e.accessToken)){let t=i?`mask replica equals the access token`:a??`no error reported (cold SW or empty reply)`;return E.error(`OAuth mask give-up: no masked value after retries`,{providerId:e.providerId,reason:t}),{error:t}}let o=t.getAccounts(),s=Re(e.providerId,e.accessToken,i,o);return s.maskedValue&&await t.saveAccounts(o),s}function ze(e){let t=C(e).oauthTokenDomains??[],n=j(e),r=new Set,i=[];for(let e of[...t,...n]){let t=e.toLowerCase();r.has(t)||(r.add(t),i.push(e))}return i}function Be(e){return new Promise(t=>{chrome.runtime.sendMessage({type:`secrets.mask-oauth-token`,...e},n=>{chrome.runtime.lastError&&E.error(`SW mask-oauth-token transport failed`,{providerId:e.providerId,error:chrome.runtime.lastError.message});let r=typeof n==`object`&&n?n:void 0;r?.error&&E.warn(`SW mask-oauth-token returned error`,{providerId:e.providerId,error:r.error}),t(r??{})})})}function Ve(e){return l(`secrets.mask-oauth-token`,e).then(t=>(t?.error&&E.warn(`Bridge mask-oauth-token returned error`,{providerId:e.providerId,error:t.error}),t??{})).catch(t=>(E.error(`Bridge mask-oauth-token transport failed`,{providerId:e.providerId,error:t instanceof Error?t.message:String(t)}),{}))}async function He(e,t,n){let r=o(`/api/secrets/oauth-update`),i=await fetch(r,{method:`POST`,headers:s({"Content-Type":`application/json`}),body:JSON.stringify({providerId:e,accessToken:t,domains:n})});if(!i.ok)return E.warn(`OAuth replica POST non-ok`,{providerId:e,status:i.status,url:r}),{error:`OAuth replica POST HTTP ${i.status}`};let a=await i.json();if(typeof a.maskedValue!=`string`)return E.warn(`OAuth replica POST ok but missing maskedValue`,{providerId:e,url:r}),{error:`OAuth replica POST ok but missing maskedValue`};if(!R(a.maskedValue,t))return E.error(`OAuth mask replica equals the access token; refusing to persist`,{providerId:e}),{error:`mask replica equals the access token`};let c=A(),l=Re(e,t,a.maskedValue,c);return l.maskedValue&&await N(c),l}async function Ue(e,t){let n=await We(e,t);if(n.error!==Le)return n;let r=A().find(t=>t.providerId===e)?.accessToken;return!r||r===t?n:We(e,r)}async function We(e,t){let n=ze(e);if(n.length===0)return{error:`no oauth token domains configured`};let r=c();try{return r===`extension-direct`?z({providerId:e,accessToken:t,domains:n},{sendMaskRequest:Be,getAccounts:A,saveAccounts:N}):r===`extension-delegate`?z({providerId:e,accessToken:t,domains:n},{sendMaskRequest:Ve,getAccounts:A,saveAccounts:N}):r===`connect`?{error:`no replica store in connect mode`}:await He(e,t,n)}catch(t){let n=t instanceof Error?t.message:String(t);return E.error(`OAuth replica sync failed`,{providerId:e,topology:r,error:n}),{error:n}}}async function Ge(e){let t=A().find(t=>t.providerId===e);return t?.accessToken?R(t.maskedValue,t.accessToken)?{maskedValue:t.maskedValue}:Ue(e,t.accessToken):{error:`no access token held`}}function Ke(e,t){if(e.accessToken)return`scopes`in e?e.scopes:t?.scopes}async function qe(e){let t=A().find(t=>t.providerId===e.providerId),n=A().filter(t=>t.providerId!==e.providerId);n.push({providerId:e.providerId,apiKey:``,accessToken:e.accessToken,refreshToken:e.refreshToken,tokenExpiresAt:e.tokenExpiresAt,userName:e.userName,userAvatar:e.userAvatar,baseUrl:e.baseUrl??t?.baseUrl,scopes:Ke(e,t)}),await N(n),await Ue(e.providerId,e.accessToken)}var Je=`local`;function B(e){let t=A().find(t=>t.providerId===e);return t&&(t.accessToken||t.apiKey)||null}function V(e){let t=A().find(t=>t.providerId===e);return t?t.accessToken||t.apiKey||(C(e).optionalApiKey?Je:null):null}function H(e){return A().find(t=>t.providerId===e)?.baseUrl??null}function Ye(e){return A().find(t=>t.providerId===e)?.deployment??null}function Xe(e){return A().find(t=>t.providerId===e)?.apiVersion??null}function U(){let e=localStorage.getItem(_)||``,t=e.indexOf(`:`);return t>=0?e.slice(t+1):e}function Ze(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t),r=new Set([...ee(),...x()]),i=n.includes(`.`);if(r.has(n)||!i){localStorage.setItem(_,e);return}}let n=W();localStorage.setItem(_,`${n}:${e}`)}function Qe(){return localStorage.getItem(_)||``}function W(){let e=Qe(),t=e.indexOf(`:`);if(t>0)return e.slice(0,t);let n=A(),r=U();if(r){let e=n.find(e=>S(e.providerId)&&Ee(e.providerId,r));if(e)return e.providerId}let i=n.find(e=>S(e.providerId));return i?i.providerId:n.length>0?n[0].providerId:`anthropic`}function $e(e){let t=U();localStorage.setItem(_,`${e}:${t}`)}function et(){let e=U();localStorage.setItem(_,e)}function tt(){return V(W())}function nt(e){let t=W();P(t,e,H(t)??void 0)}async function rt(){await F(W())}function it(){return H(W())}function at(e){let t=W(),n=B(t);n&&P(t,n,e||void 0)}function ot(){let e=W(),t=B(e);t&&P(e,t)}function st(){let e=A(),t=W(),n=U();return e.map(e=>{let r={providerId:e.providerId,apiKey:e.apiKey};return e.baseUrl&&(r.baseUrl=e.baseUrl),e.providerId===t&&n&&(r.model=n),r})}async function ct(){let e=A();await Promise.allSettled(e.map(e=>F(e.providerId))),localStorage.removeItem(g),localStorage.removeItem(_);for(let e of xe)localStorage.removeItem(e)}function lt(e){return/^(?:gpt[-.]?|o[0-9]|chatgpt)/i.test(e)?`openai`:`anthropic`}function G(e,t,n,r){return{id:t,name:t,provider:e,api:r??`${e}-${lt(t)}`,baseUrl:n??``,contextWindow:2e5,maxTokens:16384,input:[`text`,`image`],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},reasoning:!0}}function K(e,t){return t.isOAuth||e===`azure-ai-foundry`?`anthropic`:e===`bedrock-camp`?`amazon-bedrock`:e}function ut(){let e=new Set;for(let t of A())t.loggedOut||(e.add(t.providerId),e.add(K(t.providerId,C(t.providerId))));return[...e]}function dt(e,t,n){let r=T(e).find(e=>e.id===t);return r?n?{...r,baseUrl:n}:r:null}function ft(e,t,n,r){return n.isOAuth?T(t).find(e=>e.id===r)||{...e,api:`${t}-anthropic`,provider:t}:t===`bedrock-camp`?{...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`}:e}function pt(e,t,n,r,i){let a=dt(e,n,r);if(a)return a;if(t.isOAuth)return G(e,n,r);if(i)throw Error(`Model ${n} is not available from provider ${e}`);return J()}function q(e,t){if(!e)return J();let n=t??W(),r=H(n),i=C(n);if(t!==void 0){let t=dt(n,e,r);if(t)return t}try{let t=m(K(n,i),e);if(!t?.id)throw Error(`Model ${e} not found`);let a=ft(t,n,i,e);return r?{...a,baseUrl:r}:a}catch(a){return E.debug(`resolveModelById: pi-ai lookup miss, using provider fallback`,{providerId:n,modelId:e,error:a instanceof Error?a.message:String(a)}),pt(n,i,e,r,t!==void 0)}}function mt(e,t){return e.provider===t||e.provider===K(t,C(t))}function J(){let e=W(),t=U(),n=H(e),r=T(e),i=C(e),a=i.defaultModelId?r.find(e=>e.id.toLowerCase().includes(i.defaultModelId.toLowerCase()))?.id:void 0,o=t||a||r[0]?.id||`claude-sonnet-4-6`;try{let t=K(e,i),a=m(t,o);if(!a?.id)throw Error(`Model ${o} not found in ${t} registry`);let s=a;return i.isOAuth?s=r.find(e=>e.id===o)||{...s,api:`${e}-anthropic`,provider:e}:e===`bedrock-camp`&&(s={...s,api:`bedrock-camp-converse`,provider:`bedrock-camp`}),n&&(s={...s,baseUrl:n}),s}catch(t){E.debug(`resolveCurrentModel: pi-ai lookup miss, using provider fallback`,{providerId:e,effectiveModelId:o,error:t instanceof Error?t.message:String(t)});let a=r.find(e=>e.id===o);return a?n?{...a,baseUrl:n}:a:i.isOAuth?G(e,o,n):m(`anthropic`,`claude-sonnet-4-0`)}}function ht(e){let t=e.toLowerCase();if(!t)return null;let n=null;try{n=W()}catch{}if(n!==null){let e=Y(t,[n]);if(e!==null)return e}return Y(t,A().map(e=>e.providerId))}function Y(e,t){let n=null,r=-1;for(let i of t)for(let t of T(i)){let i=t.id.toLowerCase(),a=(t.name??``).toLowerCase();if(!i.includes(e)&&!a.includes(e))continue;let o=t.contextWindow??0;(o>r||o===r&&wt(t.id,n??``)>0)&&(r=o,n=t.id)}return n}function X(){let e=[];try{for(let t of A())e.includes(t.providerId)||e.push(t.providerId)}catch{}try{let t=W();e.includes(t)||e.push(t)}catch{}return e}function gt(e){let t=e.indexOf(`:`);if(t<=0||t===e.length-1)return null;let n=e.slice(0,t),r=e.slice(t+1),i=X().includes(n);if(i)return{providerId:n,modelId:r,configured:i};let a=!1;try{a=x().includes(n)}catch{}return a?{providerId:n,modelId:r,configured:!1}:null}function Z(e,t,n=!0){let r=[e],i=n?Y(e.toLowerCase(),[t]):null;i!==null&&i!==e&&r.push(i);let a=[];try{a=T(t)}catch{}for(let n of r)if(!(a.length>0&&!a.some(e=>e.id===n)))try{if(q(n,t).id===n)return{modelId:n,providerId:t}}catch(r){E.debug(`selectModelFromProvider: candidate did not resolve`,{input:e,candidate:n,providerId:t,error:r instanceof Error?r.message:String(r)})}return null}function _t(e){return e.map(e=>`${e.providerId}:${e.modelId}`).join(`, `)}function Q(e){if(!e)return{ok:!1,error:`unknown model: (empty)`};let t=gt(e);if(t)return xt(e,t);let n=null;try{n=W()}catch{}if(n!==null){let t=Z(e,n);if(t)return bt(t)}return St(e,n)}function $(){try{return W()}catch{return null}}function vt(e){let t=$();return t===null||ge(ve(),t,e.providerId,e.modelId)}function yt(e){let t=$()??`<none>`;return{ok:!1,error:`model not allowed: ${`${e.providerId}:${e.modelId}`} is blocked by ${fe} while "${t}" is selected — ${ye(t,e.providerId,e.modelId)}`}}function bt(e){return vt(e)?{ok:!0,selection:e}:yt(e)}function xt(e,t){if(!t.configured){let n=X();return{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" is not configured${n.length>0?`; configured: ${n.join(`, `)}`:``})`}}let n=Z(t.modelId,t.providerId);return n?bt(n):{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" does not offer "${t.modelId}")`}}function St(e,t){let n=X().filter(e=>e!==t);for(let t of[`exact`,`shorthand`]){let r=[];for(let i of n){let n=t===`exact`?e:Y(e.toLowerCase(),[i]);if(n===null)continue;let a=Z(n,i,!1);a&&r.push(a)}let i=r.filter(vt);if(i.length===0&&r.length>0)return yt(r[0]);if(i.length===1)return{ok:!0,selection:i[0]};if(i.length>1)return{ok:!1,error:`ambiguous model: ${e} matches ${_t(i)} — qualify it as provider:model`}}return{ok:!1,error:`unknown model: ${e}`}}function Ct(e){let t=Q(e);return t.ok?t.selection.modelId:null}function wt(e,t){let n=e.match(/\d+/g)?.map(Number)??[],r=t.match(/\d+/g)?.map(Number)??[],i=Math.max(n.length,r.length);for(let e=0;e<i;e++){let t=(n[e]??0)-(r[e]??0);if(t!==0)return t}return 0}export{de as $,W as A,F as B,j as C,T as D,C as E,b as F,Q as G,q as H,mt as I,at as J,qe as K,z as L,R as M,I as N,B as O,L as P,$e as Q,O as R,Ye as S,De as T,ht as U,J as V,Ct as W,Ne as X,M as Y,Ze as Z,V as _,ke as a,it as b,ot as c,st as d,A as f,tt as g,Me as h,P as i,D as j,U as k,et as l,Pe as m,Te as n,ct as o,k as p,nt as q,be as r,rt as s,g as t,Ge as u,Xe as v,ut as w,H as x,x as y,S as z};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./rolldown-runtime-Dd_uD5pT.js";import{t}from"./logger-LESFN7Hj.js";import"./proxied-fetch-CNe6icTW.js";import{i as n,r}from"./panel-rpc-DW7W4w6_.js";import{n as i,o as a,t as o}from"./providers-CAcycJZb.js";import{s,t as c}from"./api-endpoint-D8lUKmPu.js";import{a as l,c as u,o as ee,s as d}from"./core-aQUekdF7.js";import{t as te}from"./secrets-bridge-client-kWWipUuL.js";import{i as ne,r as re,t as ie}from"./bedrock-camp-compat-99DjWBHV.js";import{r as ae,t as oe}from"./bedrock-camp-extra-models-B0WH9IRx.js";import{s as se}from"./claude-model-version-ceOteQ2f.js";const ce=`slicc_oauth_extra_domains`;function f(e){try{let t=e.getItem(ce);if(!t)return{};let n=JSON.parse(t);if(typeof n!=`object`||!n||Array.isArray(n))return{};let r={};for(let[e,t]of Object.entries(n)){if(typeof e!=`string`||!Array.isArray(t))continue;let n=t.filter(e=>typeof e==`string`&&e.length>0);n.length>0&&(r[e]=n)}return r}catch{return{}}}function le(e,t){try{e.setItem(ce,JSON.stringify(t))}catch(e){throw Error(`Failed to persist OAuth extras (localStorage quota exceeded?): ${e instanceof Error?e.message:String(e)}`)}}function ue(e,t){let n={input:0,output:0,cacheRead:0,cacheWrite:0},r=se(e);if(!r)return n;let i;for(let e of t.values()){let t=se(e.id);!t||t.family!==r.family||(!i||t.major>i.major||t.major===i.major&&t.minor>i.minor)&&(i={major:t.major,minor:t.minor,cost:e.cost})}return i?.cost??n}const de=t(`model-policy`),p=`/etc/models`;function m(){return{sections:{}}}function fe(e){let t=e.indexOf(`#`);return(t===-1?e:e.slice(0,t)).trim()}function pe(e){let t=m();if(typeof e!=`string`)return t;let n=null;for(let r of e.split(/\r?\n/)){let e=fe(r);if(!e)continue;let i=/^\[([^\]]+)\]$/.exec(e);if(i){n=i[1].trim(),t.sections[n]??=[];continue}if(n===null){de.warn(`model policy entry before any [provider] section — ignored`,{entry:e});continue}let a=e.startsWith(`-`),o=a?e.slice(1).trim():e,s=o.indexOf(`:`);if(s<=0||s===o.length-1){de.warn(`model policy entry is not provider:model — ignored`,{entry:e});continue}t.sections[n].push({providerId:o.slice(0,s).trim(),modelId:o.slice(s+1).trim(),deny:a})}return t}function h(e,t,n){return e.providerId===t?e.modelId===`*`||e.modelId===n:!1}function me(e,t){return e.sections[t]??[]}function he(e,t,n,r){return me(e,t).some(e=>e.deny&&h(e,n,r))}function ge(e,t,n,r){let i=me(e,t);return i.some(e=>e.deny&&h(e,n,r))?!1:n===t||i.some(e=>!e.deny&&h(e,n,r))}let g=m();function _e(e){g=e}function _(){return g}function ve(e,t,n){return`add \`${t}:${n}\` (or \`${t}:*\`) under \`[${e}]\` in ${p}`}var ye=e({ACCOUNTS_KEY:()=>b,__test__:()=>Te,addAccount:()=>M,applyProviderDefaults:()=>ke,clearAllSettings:()=>ut,clearApiKey:()=>at,clearBaseUrl:()=>ct,clearSelectedProvider:()=>nt,ensureOAuthMaskReplica:()=>Je,exportProviders:()=>lt,getAccounts:()=>A,getAllAvailableModels:()=>Pe,getAllExtraOAuthDomains:()=>ze,getAlternativeModelProviders:()=>Fe,getApiKey:()=>rt,getApiKeyForProvider:()=>V,getApiVersionForProvider:()=>Qe,getAvailableProviders:()=>w,getBaseUrl:()=>ot,getBaseUrlForProvider:()=>H,getDeploymentForProvider:()=>Ze,getExtraOAuthDomains:()=>Ie,getModelCatalogProviderIds:()=>ft,getOAuthAccountInfo:()=>De,getProviderConfig:()=>E,getProviderModels:()=>O,getRawApiKeyForProvider:()=>B,getSelectedModelId:()=>U,getSelectedProvider:()=>W,isModelHiddenFromPicker:()=>je,isUsableOAuthMaskReplica:()=>R,logoutOAuthAccount:()=>F,maskOAuthTokenWithRetry:()=>I,migrateLegacyAuthOnlySelection:()=>Ce,modelRunsOnProvider:()=>gt,persistOAuthMaskViaServiceWorker:()=>z,policyVisible:()=>Ne,providerOffersLlmModels:()=>T,removeAccount:()=>P,resolveCurrentModel:()=>J,resolveModelById:()=>q,resolveModelByShorthand:()=>_t,resolveModelIdForScoop:()=>Tt,resolveModelSelectionForScoop:()=>Q,saveOAuthAccount:()=>Xe,setApiKey:()=>it,setBaseUrl:()=>st,setExtraOAuthDomains:()=>Le,setExtraOAuthDomainsAsync:()=>Re,setSelectedModelId:()=>$e,setSelectedProvider:()=>tt});const v=ee,y=d,b=`slicc_accounts`,x=`selected-model`,be=[`slicc_provider`,`slicc_api_key`,`slicc_base_url`,`anthropic_api_key`,`api_provider`,`azure_resource`,`bedrock_region`],xe=new Set([`github`]);function S(e){let t;try{t=u()}catch{return!1}return t.includes(e)&&!a(e)}let C=!1;function Se(){if(!C){C=!0;for(let e of be)try{localStorage.removeItem(e)}catch{}Ce()}}function Ce(){try{let e=localStorage.getItem(x);if(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t);(xe.has(n)||S(n))&&localStorage.removeItem(x)}}}catch{}try{let e=localStorage.getItem(b);if(!e)return;let t=JSON.parse(e);if(!Array.isArray(t))return;let n=t.filter(e=>!(typeof e==`object`&&e&&typeof e.providerId==`string`&&S(e.providerId)));n.length!==t.length&&localStorage.setItem(b,JSON.stringify(n))}catch{}}function we(){C=!1}const Te={_resetLegacyCleanup:we};function w(){let e=u().filter(a),t=i();return[...new Set([...e,...t])].filter(e=>!o(e)?.hidden)}function T(e){try{if(O(e).length>0)return!0}catch{}try{if(d(e).length>0)return!0}catch{}let t=o(e);return!!(t?.modelOverrides&&Object.keys(t.modelOverrides).length>0)}function Ee(e,t){try{if(O(e).some(e=>e.id===t))return!0}catch{}try{if(d(e).some(e=>e.id===t))return!0}catch{}let n=o(e);return!!(n?.modelOverrides&&Object.hasOwn(n.modelOverrides,t))}function E(e){return o(e)||{id:e,name:e.split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `),description:`${e} provider`,requiresApiKey:!0,requiresBaseUrl:!1}}function D(e,t){t.context_window!==void 0&&(e.contextWindow=t.context_window),t.max_tokens!==void 0&&(e.maxTokens=t.max_tokens),t.reasoning!==void 0&&(e.reasoning=t.reasoning),t.input!==void 0&&(e.input=t.input),t.cost!==void 0&&(e.cost=t.cost,e.inputCost=t.cost.input,e.outputCost=t.cost.output,e.cacheReadCost=t.cost.cacheRead,e.cacheWriteCost=t.cost.cacheWrite),t.compat!==void 0&&(e.compat={...e.compat??{},...t.compat}),t.thinkingLevelMap!==void 0&&(e.thinkingLevelMap={...e.thinkingLevelMap??{},...t.thinkingLevelMap})}function O(e){try{if(e===`bedrock-camp`){let e=ie(H(`bedrock-camp`));return ae(y(`amazon-bedrock`),oe).filter(t=>ne(t,e)).map(e=>({...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`,reasoning:e.reasoning===!0&&re(e)}))}let t=E(e);if(t.getModelIds){let n;try{n=t.getModelIds()}catch(t){return k.error(`Provider getModelIds callback failed`,{providerId:e,error:t instanceof Error?t.message:String(t)}),[]}let r=new Map;for(let e of u())try{for(let t of y(e))r.set(t.id,t)}catch{}return n.map(n=>{let i=n.api===`openai`?`openai`:`anthropic`,a=`${e}-${i}`,o=r.get(n.id),s;if(o)s={...o,api:a,provider:e};else if(s=G(e,n.id,``,a),n.name&&(s.name=n.name),i===`anthropic`){let e=ue(n.id,r);s.cost=e,s.inputCost=e.input,s.outputCost=e.output,s.cacheReadCost=e.cacheRead,s.cacheWriteCost=e.cacheWrite}let c=t.modelOverrides?.[n.id];return c&&D(s,c),D(s,n),s})}if(t.isOAuth){let n=y(`anthropic`),r=`${e}-anthropic`;return n.map(n=>{let i={...n,api:r,provider:e},a=t.modelOverrides?.[n.id];return a&&D(i,a),i})}return y(e===`azure-ai-foundry`?`anthropic`:e)}catch(t){return k.error(`Failed to load models`,{providerId:e,error:t instanceof Error?t.message:String(t)}),[]}}function De(e){let t=A().find(t=>t.providerId===e);if(!t?.accessToken)return null;let n=!!t.tokenExpiresAt&&Date.now()>t.tokenExpiresAt-6e4;return{token:t.accessToken,maskedValue:t.maskedValue,expiresAt:t.tokenExpiresAt,userName:t.userName,userAvatar:t.userAvatar,scopes:t.scopes,expired:n}}const Oe=Object.assign({})[`/packages/webapp/providers.json`]??[],k=t(`provider-settings`);function ke(e=Oe){if(e.length===0||A().length>0)return;let t=new Set(w());for(let n of e)if(!(!n.providerId||!n.apiKey)){if(!t.has(n.providerId)){k.warn(`Unknown provider "${n.providerId}" in providers.json — skipping`);continue}M(n.providerId,n.apiKey,n.baseUrl)}let n=e.find(e=>e.providerId&&e.apiKey&&t.has(e.providerId));n?.model&&!localStorage.getItem(x)&&localStorage.setItem(x,`${n.providerId}:${n.model}`)}const Ae=[/haiku/i];function je(e){return Ae.some(t=>t.test(e))}function Me(e){return e.filter(e=>!je(e.id))}function Ne(e,t){let n=$();if(n===null)return e;let r=_();return e.filter(e=>!he(r,n,t,e.id))}function Pe(){let e=A();if(e.length===0)return[];let t=new Map;for(let n of e){if(t.has(n.providerId))continue;let e=E(n.providerId);if(e.hidden||S(n.providerId))continue;let r=Ne(Me(O(n.providerId)),n.providerId);if(r.length===0)continue;let i={providerId:n.providerId,providerName:e.name,models:r};t.set(n.providerId,i)}return[...t.values()]}function Fe(e){return Pe().map(e=>e.providerId).filter(t=>t!==e)}function A(){Se();let e=localStorage.getItem(b);if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.providerId==`string`&&typeof e.apiKey==`string`):[]}catch{return[]}}function Ie(e){return f(localStorage)[e]??[]}function Le(e,t){let n=f(localStorage),r=t.map(e=>e.trim()).filter(e=>e.length>0);r.length===0?delete n[e]:n[e]=r,le(localStorage,n)}async function Re(e,t){if(n()){Le(e,t);return}let i=r();if(!i)throw Error(`setExtraOAuthDomainsAsync: no DOM and no panel-rpc client — cannot persist to page localStorage`);let{storeAfter:a}=await i.call(`oauth-extras-set`,{providerId:e,domains:t});try{le(localStorage,a)}catch(t){k.warn(`worker-shim mirror failed after successful page write — reload to refresh`,{providerId:e,error:t instanceof Error?t.message:String(t)})}}function ze(){return f(localStorage)}function Be(e){localStorage.setItem(b,JSON.stringify(e))}async function j(e){if(n()){Be(e);return}let t=r();if(!t)throw Error(`saveAccountsAsync: no DOM and no panel-rpc client — cannot persist to page localStorage`);let i=JSON.stringify(e),{storedJson:a}=await t.call(`save-oauth-accounts`,{accountsJson:i});try{localStorage.setItem(b,a)}catch(e){k.warn(`worker-shim mirror failed after successful page write — reload to refresh`,{error:e instanceof Error?e.message:String(e)})}}function M(e,t,n,r,i){let a=A().filter(t=>t.providerId!==e),o={providerId:e,apiKey:t};n&&(o.baseUrl=n),r&&(o.deployment=r),i&&(o.apiVersion=i),a.push(o),Be(a)}async function N(e){let t=l();try{if(t===`extension-direct`){let t=await new Promise(t=>{chrome.runtime.sendMessage({type:`secrets.delete`,name:`oauth.${e}.token`},n=>{chrome.runtime.lastError&&k.error(`SW secrets.delete transport failed`,{providerId:e,error:chrome.runtime.lastError.message}),t(n??{})})});t.error&&k.error(`SW secrets.delete returned error`,{providerId:e,error:t.error})}else if(t===`extension-delegate`){let t=await te(`secrets.delete`,{name:`oauth.${e}.token`});t?.error&&k.error(`Bridge secrets.delete returned error`,{providerId:e,error:t.error})}else if(t!==`connect`){let t=await fetch(s(`/api/secrets/oauth/${e}`),{method:`DELETE`,headers:c()});!t.ok&&t.status!==404&&k.warn(`OAuth replica DELETE non-ok`,{providerId:e,status:t.status})}}catch(n){k.error(`OAuth replica removal failed`,{providerId:e,topology:t,error:n instanceof Error?n.message:String(n)})}}async function P(e){let t=A().find(t=>t.providerId===e),n=E(e);t&&n?.isOAuth&&await F(e),await N(e),await j(A().filter(t=>t.providerId!==e));let r=localStorage.getItem(x)??``,i=r.indexOf(`:`);i>0&&r.slice(0,i)===e&&localStorage.removeItem(x)}async function F(e){let t=A().find(t=>t.providerId===e);if(!t)return;let n=E(e);if(n?.isOAuth){if(n.onOAuthLogout)try{await n.onOAuthLogout()}catch(t){k.warn(`onOAuthLogout failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})}if(n.getOAuthLogoutUrl){let r=n.getOAuthLogoutUrl(t);if(r){let{openIdpLogoutUrl:t}=await import(`./oauth-service-CxXBA9rC.js`);await t(r).catch(t=>{k.warn(`IdP logout popup failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})})}}await j(A().map(t=>t.providerId===e?{providerId:t.providerId,apiKey:``,baseUrl:t.baseUrl,userName:t.userName,userAvatar:t.userAvatar,loggedOut:!0}:t)),await N(e)}}async function I(e,t={}){let n=Math.max(1,t.attempts??3),r=t.delayMs??150,i=t.sleep??(e=>new Promise(t=>setTimeout(t,e))),a;for(let t=0;t<n;t++){let o=await e();if(o.maskedValue)return{maskedValue:o.maskedValue};o.error&&(a=o.error),t<n-1&&await i(r)}return{lastError:a}}const L=`access token rotated during mask write`;function Ve(e,t,n,r){let i=r.find(t=>t.providerId===e);return i?.accessToken?i.accessToken===t?(i.maskedValue=n,{maskedValue:n}):(k.warn(`OAuth mask write raced with token rotation; discarding replica`,{providerId:e}),{error:L}):{error:`account gone after mask write`}}function R(e,t){return!!e&&e!==t}async function z(e,t,n){let r={providerId:e.providerId,accessToken:e.accessToken,domains:e.domains.join(`,`)},{maskedValue:i,lastError:a}=await I(()=>t.sendMaskRequest(r),n);if(!R(i,e.accessToken)){let t=i?`mask replica equals the access token`:a??`no error reported (cold SW or empty reply)`;return k.error(`OAuth mask give-up: no masked value after retries`,{providerId:e.providerId,reason:t}),{error:t}}let o=t.getAccounts(),s=Ve(e.providerId,e.accessToken,i,o);return s.maskedValue&&await t.saveAccounts(o),s}function He(e){let t=E(e).oauthTokenDomains??[],n=Ie(e),r=new Set,i=[];for(let e of[...t,...n]){let t=e.toLowerCase();r.has(t)||(r.add(t),i.push(e))}return i}function Ue(e){return new Promise(t=>{chrome.runtime.sendMessage({type:`secrets.mask-oauth-token`,...e},n=>{chrome.runtime.lastError&&k.error(`SW mask-oauth-token transport failed`,{providerId:e.providerId,error:chrome.runtime.lastError.message});let r=typeof n==`object`&&n?n:void 0;r?.error&&k.warn(`SW mask-oauth-token returned error`,{providerId:e.providerId,error:r.error}),t(r??{})})})}function We(e){return te(`secrets.mask-oauth-token`,e).then(t=>(t?.error&&k.warn(`Bridge mask-oauth-token returned error`,{providerId:e.providerId,error:t.error}),t??{})).catch(t=>(k.error(`Bridge mask-oauth-token transport failed`,{providerId:e.providerId,error:t instanceof Error?t.message:String(t)}),{}))}async function Ge(e,t,n){let r=s(`/api/secrets/oauth-update`),i=await fetch(r,{method:`POST`,headers:c({"Content-Type":`application/json`}),body:JSON.stringify({providerId:e,accessToken:t,domains:n})});if(!i.ok)return k.warn(`OAuth replica POST non-ok`,{providerId:e,status:i.status,url:r}),{error:`OAuth replica POST HTTP ${i.status}`};let a=await i.json();if(typeof a.maskedValue!=`string`)return k.warn(`OAuth replica POST ok but missing maskedValue`,{providerId:e,url:r}),{error:`OAuth replica POST ok but missing maskedValue`};if(!R(a.maskedValue,t))return k.error(`OAuth mask replica equals the access token; refusing to persist`,{providerId:e}),{error:`mask replica equals the access token`};let o=A(),l=Ve(e,t,a.maskedValue,o);return l.maskedValue&&await j(o),l}async function Ke(e,t){let n=await qe(e,t);if(n.error!==L)return n;let r=A().find(t=>t.providerId===e)?.accessToken;return!r||r===t?n:qe(e,r)}async function qe(e,t){let n=He(e);if(n.length===0)return{error:`no oauth token domains configured`};let r=l();try{return r===`extension-direct`?z({providerId:e,accessToken:t,domains:n},{sendMaskRequest:Ue,getAccounts:A,saveAccounts:j}):r===`extension-delegate`?z({providerId:e,accessToken:t,domains:n},{sendMaskRequest:We,getAccounts:A,saveAccounts:j}):r===`connect`?{error:`no replica store in connect mode`}:await Ge(e,t,n)}catch(t){let n=t instanceof Error?t.message:String(t);return k.error(`OAuth replica sync failed`,{providerId:e,topology:r,error:n}),{error:n}}}async function Je(e){let t=A().find(t=>t.providerId===e);return t?.accessToken?R(t.maskedValue,t.accessToken)?{maskedValue:t.maskedValue}:Ke(e,t.accessToken):{error:`no access token held`}}function Ye(e,t){if(e.accessToken)return`scopes`in e?e.scopes:t?.scopes}async function Xe(e){let t=A().find(t=>t.providerId===e.providerId),n=A().filter(t=>t.providerId!==e.providerId);n.push({providerId:e.providerId,apiKey:``,accessToken:e.accessToken,refreshToken:e.refreshToken,tokenExpiresAt:e.tokenExpiresAt,userName:e.userName,userAvatar:e.userAvatar,baseUrl:e.baseUrl??t?.baseUrl,scopes:Ye(e,t)}),await j(n),await Ke(e.providerId,e.accessToken)}function B(e){let t=A().find(t=>t.providerId===e);return t&&(t.accessToken||t.apiKey)||null}function V(e){let t=A().find(t=>t.providerId===e);return t?t.accessToken||t.apiKey||(E(e).optionalApiKey?`local`:null):null}function H(e){return A().find(t=>t.providerId===e)?.baseUrl??null}function Ze(e){return A().find(t=>t.providerId===e)?.deployment??null}function Qe(e){return A().find(t=>t.providerId===e)?.apiVersion??null}function U(){let e=localStorage.getItem(x)||``,t=e.indexOf(`:`);return t>=0?e.slice(t+1):e}function $e(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t),r=new Set([...i(),...w()]),a=n.includes(`.`);if(r.has(n)||!a){localStorage.setItem(x,e);return}}let n=W();localStorage.setItem(x,`${n}:${e}`)}function et(){return localStorage.getItem(x)||``}function W(){let e=et(),t=e.indexOf(`:`);if(t>0)return e.slice(0,t);let n=A(),r=U();if(r){let e=n.find(e=>T(e.providerId)&&Ee(e.providerId,r));if(e)return e.providerId}let i=n.find(e=>T(e.providerId));return i?i.providerId:n.length>0?n[0].providerId:`anthropic`}function tt(e){let t=U();localStorage.setItem(x,`${e}:${t}`)}function nt(){let e=U();localStorage.setItem(x,e)}function rt(){return V(W())}function it(e){let t=W();M(t,e,H(t)??void 0)}async function at(){await P(W())}function ot(){return H(W())}function st(e){let t=W(),n=B(t);n&&M(t,n,e||void 0)}function ct(){let e=W(),t=B(e);t&&M(e,t)}function lt(){let e=A(),t=W(),n=U();return e.map(e=>{let r={providerId:e.providerId,apiKey:e.apiKey};return e.baseUrl&&(r.baseUrl=e.baseUrl),e.providerId===t&&n&&(r.model=n),r})}async function ut(){let e=A();await Promise.allSettled(e.map(e=>P(e.providerId))),localStorage.removeItem(b),localStorage.removeItem(x);for(let e of be)localStorage.removeItem(e)}function dt(e){return/^(?:gpt[-.]?|o[0-9]|chatgpt)/i.test(e)?`openai`:`anthropic`}function G(e,t,n,r){return{id:t,name:t,provider:e,api:r??`${e}-${dt(t)}`,baseUrl:n??``,contextWindow:2e5,maxTokens:16384,input:[`text`,`image`],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},reasoning:!0}}function K(e,t){return t.isOAuth||e===`azure-ai-foundry`?`anthropic`:e===`bedrock-camp`?`amazon-bedrock`:e}function ft(){let e=new Set;for(let t of A())t.loggedOut||(e.add(t.providerId),e.add(K(t.providerId,E(t.providerId))));return[...e]}function pt(e,t,n){let r=O(e).find(e=>e.id===t);return r?n?{...r,baseUrl:n}:r:null}function mt(e,t,n,r){return n.isOAuth?O(t).find(e=>e.id===r)||{...e,api:`${t}-anthropic`,provider:t}:t===`bedrock-camp`?{...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`}:e}function ht(e,t,n,r,i){let a=pt(e,n,r);if(a)return a;if(t.isOAuth)return G(e,n,r);if(i)throw Error(`Model ${n} is not available from provider ${e}`);return J()}function q(e,t){if(!e)return J();let n=t??W(),r=H(n),i=E(n);if(t!==void 0){let t=pt(n,e,r);if(t)return t}try{let t=K(n,i),a=v(t,e);if(!a?.id)throw Error(`Model ${e} not found`);let o=mt(a,n,i,e);return r?{...o,baseUrl:r}:o}catch(a){return k.debug(`resolveModelById: pi-ai lookup miss, using provider fallback`,{providerId:n,modelId:e,error:a instanceof Error?a.message:String(a)}),ht(n,i,e,r,t!==void 0)}}function gt(e,t){return e.provider===t||e.provider===K(t,E(t))}function J(){let e=W(),t=U(),n=H(e),r=O(e),i=E(e),a=i.defaultModelId?r.find(e=>e.id.toLowerCase().includes(i.defaultModelId.toLowerCase()))?.id:void 0,o=t||a||r[0]?.id||`claude-sonnet-4-6`;try{let t=K(e,i),a=v(t,o);if(!a?.id)throw Error(`Model ${o} not found in ${t} registry`);let s=a;return i.isOAuth?s=r.find(e=>e.id===o)||{...s,api:`${e}-anthropic`,provider:e}:e===`bedrock-camp`&&(s={...s,api:`bedrock-camp-converse`,provider:`bedrock-camp`}),n&&(s={...s,baseUrl:n}),s}catch(t){k.debug(`resolveCurrentModel: pi-ai lookup miss, using provider fallback`,{providerId:e,effectiveModelId:o,error:t instanceof Error?t.message:String(t)});let a=r.find(e=>e.id===o);return a?n?{...a,baseUrl:n}:a:i.isOAuth?G(e,o,n):v(`anthropic`,`claude-sonnet-4-0`)}}function _t(e){let t=e.toLowerCase();if(!t)return null;let n=null;try{n=W()}catch{}if(n!==null){let e=Y(t,[n]);if(e!==null)return e}return Y(t,A().map(e=>e.providerId))}function Y(e,t){let n=null,r=-1;for(let i of t)for(let t of O(i)){let i=t.id.toLowerCase(),a=(t.name??``).toLowerCase();if(!i.includes(e)&&!a.includes(e))continue;let o=t.contextWindow??0;(o>r||o===r&&Et(t.id,n??``)>0)&&(r=o,n=t.id)}return n}function X(){let e=[];try{for(let t of A())e.includes(t.providerId)||e.push(t.providerId)}catch{}try{let t=W();e.includes(t)||e.push(t)}catch{}return e}function vt(e){let t=e.indexOf(`:`);if(t<=0||t===e.length-1)return null;let n=e.slice(0,t),r=e.slice(t+1),i=X().includes(n);if(i)return{providerId:n,modelId:r,configured:i};let a=!1;try{a=w().includes(n)}catch{}return a?{providerId:n,modelId:r,configured:!1}:null}function Z(e,t,n=!0){let r=[e],i=n?Y(e.toLowerCase(),[t]):null;i!==null&&i!==e&&r.push(i);let a=[];try{a=O(t)}catch{}for(let n of r)if(!(a.length>0&&!a.some(e=>e.id===n)))try{if(q(n,t).id===n)return{modelId:n,providerId:t}}catch(r){k.debug(`selectModelFromProvider: candidate did not resolve`,{input:e,candidate:n,providerId:t,error:r instanceof Error?r.message:String(r)})}return null}function yt(e){return e.map(e=>`${e.providerId}:${e.modelId}`).join(`, `)}function Q(e){if(!e)return{ok:!1,error:`unknown model: (empty)`};let t=vt(e);if(t)return Ct(e,t);let n=null;try{n=W()}catch{}if(n!==null){let t=Z(e,n);if(t)return St(t)}return wt(e,n)}function $(){try{return W()}catch{return null}}function bt(e){let t=$();return t===null||ge(_(),t,e.providerId,e.modelId)}function xt(e){let t=$()??`<none>`;return{ok:!1,error:`model not allowed: ${`${e.providerId}:${e.modelId}`} is blocked by ${p} while "${t}" is selected — ${ve(t,e.providerId,e.modelId)}`}}function St(e){return bt(e)?{ok:!0,selection:e}:xt(e)}function Ct(e,t){if(!t.configured){let n=X();return{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" is not configured${n.length>0?`; configured: ${n.join(`, `)}`:``})`}}let n=Z(t.modelId,t.providerId);return n?St(n):{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" does not offer "${t.modelId}")`}}function wt(e,t){let n=X().filter(e=>e!==t);for(let t of[`exact`,`shorthand`]){let r=[];for(let i of n){let n=t===`exact`?e:Y(e.toLowerCase(),[i]);if(n===null)continue;let a=Z(n,i,!1);a&&r.push(a)}let i=r.filter(bt);if(i.length===0&&r.length>0)return xt(r[0]);if(i.length===1)return{ok:!0,selection:i[0]};if(i.length>1)return{ok:!1,error:`ambiguous model: ${e} matches ${yt(i)} — qualify it as provider:model`}}return{ok:!1,error:`unknown model: ${e}`}}function Tt(e){let t=Q(e);return t.ok?t.selection.modelId:null}function Et(e,t){let n=e.match(/\d+/g)?.map(Number)??[],r=t.match(/\d+/g)?.map(Number)??[],i=Math.max(n.length,r.length);for(let e=0;e<i;e++){let t=(n[e]??0)-(r[e]??0);if(t!==0)return t}return 0}export{ue as S,Xe as _,V as a,pe as b,Ze as c,U as d,W as f,Q as g,q as h,rt as i,De as l,J as m,Je as n,Qe as o,gt as p,A as r,H as s,ye as t,O as u,p as v,_e as x,m as y};
|
|
1
|
+
import{n as e}from"./rolldown-runtime-Dd_uD5pT.js";import{t}from"./logger-LESFN7Hj.js";import"./proxied-fetch-J19bK2dG.js";import{i as n,r}from"./panel-rpc-DW7W4w6_.js";import{n as i,o as a,t as o}from"./providers-DLk_1jra.js";import{s,t as c}from"./api-endpoint-D8lUKmPu.js";import{a as l,c as u,o as ee,s as d}from"./core-D-Fp1HL2.js";import{t as te}from"./secrets-bridge-client-CUClOhLJ.js";import{i as ne,r as re,t as ie}from"./bedrock-camp-compat-99DjWBHV.js";import{r as ae,t as oe}from"./bedrock-camp-extra-models-B0WH9IRx.js";import{s as se}from"./claude-model-version-ceOteQ2f.js";const ce=`slicc_oauth_extra_domains`;function f(e){try{let t=e.getItem(ce);if(!t)return{};let n=JSON.parse(t);if(typeof n!=`object`||!n||Array.isArray(n))return{};let r={};for(let[e,t]of Object.entries(n)){if(typeof e!=`string`||!Array.isArray(t))continue;let n=t.filter(e=>typeof e==`string`&&e.length>0);n.length>0&&(r[e]=n)}return r}catch{return{}}}function le(e,t){try{e.setItem(ce,JSON.stringify(t))}catch(e){throw Error(`Failed to persist OAuth extras (localStorage quota exceeded?): ${e instanceof Error?e.message:String(e)}`)}}function ue(e,t){let n={input:0,output:0,cacheRead:0,cacheWrite:0},r=se(e);if(!r)return n;let i;for(let e of t.values()){let t=se(e.id);!t||t.family!==r.family||(!i||t.major>i.major||t.major===i.major&&t.minor>i.minor)&&(i={major:t.major,minor:t.minor,cost:e.cost})}return i?.cost??n}const de=t(`model-policy`),p=`/etc/models`;function m(){return{sections:{}}}function fe(e){let t=e.indexOf(`#`);return(t===-1?e:e.slice(0,t)).trim()}function pe(e){let t=m();if(typeof e!=`string`)return t;let n=null;for(let r of e.split(/\r?\n/)){let e=fe(r);if(!e)continue;let i=/^\[([^\]]+)\]$/.exec(e);if(i){n=i[1].trim(),t.sections[n]??=[];continue}if(n===null){de.warn(`model policy entry before any [provider] section — ignored`,{entry:e});continue}let a=e.startsWith(`-`),o=a?e.slice(1).trim():e,s=o.indexOf(`:`);if(s<=0||s===o.length-1){de.warn(`model policy entry is not provider:model — ignored`,{entry:e});continue}t.sections[n].push({providerId:o.slice(0,s).trim(),modelId:o.slice(s+1).trim(),deny:a})}return t}function h(e,t,n){return e.providerId===t?e.modelId===`*`||e.modelId===n:!1}function me(e,t){return e.sections[t]??[]}function he(e,t,n,r){return me(e,t).some(e=>e.deny&&h(e,n,r))}function ge(e,t,n,r){let i=me(e,t);return i.some(e=>e.deny&&h(e,n,r))?!1:n===t||i.some(e=>!e.deny&&h(e,n,r))}let g=m();function _e(e){g=e}function _(){return g}function ve(e,t,n){return`add \`${t}:${n}\` (or \`${t}:*\`) under \`[${e}]\` in ${p}`}var ye=e({ACCOUNTS_KEY:()=>b,__test__:()=>Te,addAccount:()=>M,applyProviderDefaults:()=>ke,clearAllSettings:()=>ut,clearApiKey:()=>at,clearBaseUrl:()=>ct,clearSelectedProvider:()=>nt,ensureOAuthMaskReplica:()=>Je,exportProviders:()=>lt,getAccounts:()=>A,getAllAvailableModels:()=>Pe,getAllExtraOAuthDomains:()=>ze,getAlternativeModelProviders:()=>Fe,getApiKey:()=>rt,getApiKeyForProvider:()=>V,getApiVersionForProvider:()=>Qe,getAvailableProviders:()=>w,getBaseUrl:()=>ot,getBaseUrlForProvider:()=>H,getDeploymentForProvider:()=>Ze,getExtraOAuthDomains:()=>Ie,getModelCatalogProviderIds:()=>ft,getOAuthAccountInfo:()=>De,getProviderConfig:()=>E,getProviderModels:()=>O,getRawApiKeyForProvider:()=>B,getSelectedModelId:()=>U,getSelectedProvider:()=>W,isModelHiddenFromPicker:()=>je,isUsableOAuthMaskReplica:()=>R,logoutOAuthAccount:()=>F,maskOAuthTokenWithRetry:()=>I,migrateLegacyAuthOnlySelection:()=>Ce,modelRunsOnProvider:()=>gt,persistOAuthMaskViaServiceWorker:()=>z,policyVisible:()=>Ne,providerOffersLlmModels:()=>T,removeAccount:()=>P,resolveCurrentModel:()=>J,resolveModelById:()=>q,resolveModelByShorthand:()=>_t,resolveModelIdForScoop:()=>Tt,resolveModelSelectionForScoop:()=>Q,saveOAuthAccount:()=>Xe,setApiKey:()=>it,setBaseUrl:()=>st,setExtraOAuthDomains:()=>Le,setExtraOAuthDomainsAsync:()=>Re,setSelectedModelId:()=>$e,setSelectedProvider:()=>tt});const v=ee,y=d,b=`slicc_accounts`,x=`selected-model`,be=[`slicc_provider`,`slicc_api_key`,`slicc_base_url`,`anthropic_api_key`,`api_provider`,`azure_resource`,`bedrock_region`],xe=new Set([`github`]);function S(e){let t;try{t=u()}catch{return!1}return t.includes(e)&&!a(e)}let C=!1;function Se(){if(!C){C=!0;for(let e of be)try{localStorage.removeItem(e)}catch{}Ce()}}function Ce(){try{let e=localStorage.getItem(x);if(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t);(xe.has(n)||S(n))&&localStorage.removeItem(x)}}}catch{}try{let e=localStorage.getItem(b);if(!e)return;let t=JSON.parse(e);if(!Array.isArray(t))return;let n=t.filter(e=>!(typeof e==`object`&&e&&typeof e.providerId==`string`&&S(e.providerId)));n.length!==t.length&&localStorage.setItem(b,JSON.stringify(n))}catch{}}function we(){C=!1}const Te={_resetLegacyCleanup:we};function w(){let e=u().filter(a),t=i();return[...new Set([...e,...t])].filter(e=>!o(e)?.hidden)}function T(e){try{if(O(e).length>0)return!0}catch{}try{if(d(e).length>0)return!0}catch{}let t=o(e);return!!(t?.modelOverrides&&Object.keys(t.modelOverrides).length>0)}function Ee(e,t){try{if(O(e).some(e=>e.id===t))return!0}catch{}try{if(d(e).some(e=>e.id===t))return!0}catch{}let n=o(e);return!!(n?.modelOverrides&&Object.hasOwn(n.modelOverrides,t))}function E(e){return o(e)||{id:e,name:e.split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `),description:`${e} provider`,requiresApiKey:!0,requiresBaseUrl:!1}}function D(e,t){t.context_window!==void 0&&(e.contextWindow=t.context_window),t.max_tokens!==void 0&&(e.maxTokens=t.max_tokens),t.reasoning!==void 0&&(e.reasoning=t.reasoning),t.input!==void 0&&(e.input=t.input),t.cost!==void 0&&(e.cost=t.cost,e.inputCost=t.cost.input,e.outputCost=t.cost.output,e.cacheReadCost=t.cost.cacheRead,e.cacheWriteCost=t.cost.cacheWrite),t.compat!==void 0&&(e.compat={...e.compat??{},...t.compat}),t.thinkingLevelMap!==void 0&&(e.thinkingLevelMap={...e.thinkingLevelMap??{},...t.thinkingLevelMap})}function O(e){try{if(e===`bedrock-camp`){let e=ie(H(`bedrock-camp`));return ae(y(`amazon-bedrock`),oe).filter(t=>ne(t,e)).map(e=>({...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`,reasoning:e.reasoning===!0&&re(e)}))}let t=E(e);if(t.getModelIds){let n;try{n=t.getModelIds()}catch(t){return k.error(`Provider getModelIds callback failed`,{providerId:e,error:t instanceof Error?t.message:String(t)}),[]}let r=new Map;for(let e of u())try{for(let t of y(e))r.set(t.id,t)}catch{}return n.map(n=>{let i=n.api===`openai`?`openai`:`anthropic`,a=`${e}-${i}`,o=r.get(n.id),s;if(o)s={...o,api:a,provider:e};else if(s=G(e,n.id,``,a),n.name&&(s.name=n.name),i===`anthropic`){let e=ue(n.id,r);s.cost=e,s.inputCost=e.input,s.outputCost=e.output,s.cacheReadCost=e.cacheRead,s.cacheWriteCost=e.cacheWrite}let c=t.modelOverrides?.[n.id];return c&&D(s,c),D(s,n),s})}if(t.isOAuth){let n=y(`anthropic`),r=`${e}-anthropic`;return n.map(n=>{let i={...n,api:r,provider:e},a=t.modelOverrides?.[n.id];return a&&D(i,a),i})}return y(e===`azure-ai-foundry`?`anthropic`:e)}catch(t){return k.error(`Failed to load models`,{providerId:e,error:t instanceof Error?t.message:String(t)}),[]}}function De(e){let t=A().find(t=>t.providerId===e);if(!t?.accessToken)return null;let n=!!t.tokenExpiresAt&&Date.now()>t.tokenExpiresAt-6e4;return{token:t.accessToken,maskedValue:t.maskedValue,expiresAt:t.tokenExpiresAt,userName:t.userName,userAvatar:t.userAvatar,scopes:t.scopes,expired:n}}const Oe=Object.assign({})[`/packages/webapp/providers.json`]??[],k=t(`provider-settings`);function ke(e=Oe){if(e.length===0||A().length>0)return;let t=new Set(w());for(let n of e)if(!(!n.providerId||!n.apiKey)){if(!t.has(n.providerId)){k.warn(`Unknown provider "${n.providerId}" in providers.json — skipping`);continue}M(n.providerId,n.apiKey,n.baseUrl)}let n=e.find(e=>e.providerId&&e.apiKey&&t.has(e.providerId));n?.model&&!localStorage.getItem(x)&&localStorage.setItem(x,`${n.providerId}:${n.model}`)}const Ae=[/haiku/i];function je(e){return Ae.some(t=>t.test(e))}function Me(e){return e.filter(e=>!je(e.id))}function Ne(e,t){let n=$();if(n===null)return e;let r=_();return e.filter(e=>!he(r,n,t,e.id))}function Pe(){let e=A();if(e.length===0)return[];let t=new Map;for(let n of e){if(t.has(n.providerId))continue;let e=E(n.providerId);if(e.hidden||S(n.providerId))continue;let r=Ne(Me(O(n.providerId)),n.providerId);if(r.length===0)continue;let i={providerId:n.providerId,providerName:e.name,models:r};t.set(n.providerId,i)}return[...t.values()]}function Fe(e){return Pe().map(e=>e.providerId).filter(t=>t!==e)}function A(){Se();let e=localStorage.getItem(b);if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.providerId==`string`&&typeof e.apiKey==`string`):[]}catch{return[]}}function Ie(e){return f(localStorage)[e]??[]}function Le(e,t){let n=f(localStorage),r=t.map(e=>e.trim()).filter(e=>e.length>0);r.length===0?delete n[e]:n[e]=r,le(localStorage,n)}async function Re(e,t){if(n()){Le(e,t);return}let i=r();if(!i)throw Error(`setExtraOAuthDomainsAsync: no DOM and no panel-rpc client — cannot persist to page localStorage`);let{storeAfter:a}=await i.call(`oauth-extras-set`,{providerId:e,domains:t});try{le(localStorage,a)}catch(t){k.warn(`worker-shim mirror failed after successful page write — reload to refresh`,{providerId:e,error:t instanceof Error?t.message:String(t)})}}function ze(){return f(localStorage)}function Be(e){localStorage.setItem(b,JSON.stringify(e))}async function j(e){if(n()){Be(e);return}let t=r();if(!t)throw Error(`saveAccountsAsync: no DOM and no panel-rpc client — cannot persist to page localStorage`);let i=JSON.stringify(e),{storedJson:a}=await t.call(`save-oauth-accounts`,{accountsJson:i});try{localStorage.setItem(b,a)}catch(e){k.warn(`worker-shim mirror failed after successful page write — reload to refresh`,{error:e instanceof Error?e.message:String(e)})}}function M(e,t,n,r,i){let a=A().filter(t=>t.providerId!==e),o={providerId:e,apiKey:t};n&&(o.baseUrl=n),r&&(o.deployment=r),i&&(o.apiVersion=i),a.push(o),Be(a)}async function N(e){let t=l();try{if(t===`extension-direct`){let t=await new Promise(t=>{chrome.runtime.sendMessage({type:`secrets.delete`,name:`oauth.${e}.token`},n=>{chrome.runtime.lastError&&k.error(`SW secrets.delete transport failed`,{providerId:e,error:chrome.runtime.lastError.message}),t(n??{})})});t.error&&k.error(`SW secrets.delete returned error`,{providerId:e,error:t.error})}else if(t===`extension-delegate`){let t=await te(`secrets.delete`,{name:`oauth.${e}.token`});t?.error&&k.error(`Bridge secrets.delete returned error`,{providerId:e,error:t.error})}else if(t!==`connect`){let t=await fetch(s(`/api/secrets/oauth/${e}`),{method:`DELETE`,headers:c()});!t.ok&&t.status!==404&&k.warn(`OAuth replica DELETE non-ok`,{providerId:e,status:t.status})}}catch(n){k.error(`OAuth replica removal failed`,{providerId:e,topology:t,error:n instanceof Error?n.message:String(n)})}}async function P(e){let t=A().find(t=>t.providerId===e),n=E(e);t&&n?.isOAuth&&await F(e),await N(e),await j(A().filter(t=>t.providerId!==e));let r=localStorage.getItem(x)??``,i=r.indexOf(`:`);i>0&&r.slice(0,i)===e&&localStorage.removeItem(x)}async function F(e){let t=A().find(t=>t.providerId===e);if(!t)return;let n=E(e);if(n?.isOAuth){if(n.onOAuthLogout)try{await n.onOAuthLogout()}catch(t){k.warn(`onOAuthLogout failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})}if(n.getOAuthLogoutUrl){let r=n.getOAuthLogoutUrl(t);if(r){let{openIdpLogoutUrl:t}=await import(`./oauth-service-Dn_Xz1FZ.js`);await t(r).catch(t=>{k.warn(`IdP logout popup failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})})}}await j(A().map(t=>t.providerId===e?{providerId:t.providerId,apiKey:``,baseUrl:t.baseUrl,userName:t.userName,userAvatar:t.userAvatar,loggedOut:!0}:t)),await N(e)}}async function I(e,t={}){let n=Math.max(1,t.attempts??3),r=t.delayMs??150,i=t.sleep??(e=>new Promise(t=>setTimeout(t,e))),a;for(let t=0;t<n;t++){let o=await e();if(o.maskedValue)return{maskedValue:o.maskedValue};o.error&&(a=o.error),t<n-1&&await i(r)}return{lastError:a}}const L=`access token rotated during mask write`;function Ve(e,t,n,r){let i=r.find(t=>t.providerId===e);return i?.accessToken?i.accessToken===t?(i.maskedValue=n,{maskedValue:n}):(k.warn(`OAuth mask write raced with token rotation; discarding replica`,{providerId:e}),{error:L}):{error:`account gone after mask write`}}function R(e,t){return!!e&&e!==t}async function z(e,t,n){let r={providerId:e.providerId,accessToken:e.accessToken,domains:e.domains.join(`,`)},{maskedValue:i,lastError:a}=await I(()=>t.sendMaskRequest(r),n);if(!R(i,e.accessToken)){let t=i?`mask replica equals the access token`:a??`no error reported (cold SW or empty reply)`;return k.error(`OAuth mask give-up: no masked value after retries`,{providerId:e.providerId,reason:t}),{error:t}}let o=t.getAccounts(),s=Ve(e.providerId,e.accessToken,i,o);return s.maskedValue&&await t.saveAccounts(o),s}function He(e){let t=E(e).oauthTokenDomains??[],n=Ie(e),r=new Set,i=[];for(let e of[...t,...n]){let t=e.toLowerCase();r.has(t)||(r.add(t),i.push(e))}return i}function Ue(e){return new Promise(t=>{chrome.runtime.sendMessage({type:`secrets.mask-oauth-token`,...e},n=>{chrome.runtime.lastError&&k.error(`SW mask-oauth-token transport failed`,{providerId:e.providerId,error:chrome.runtime.lastError.message});let r=typeof n==`object`&&n?n:void 0;r?.error&&k.warn(`SW mask-oauth-token returned error`,{providerId:e.providerId,error:r.error}),t(r??{})})})}function We(e){return te(`secrets.mask-oauth-token`,e).then(t=>(t?.error&&k.warn(`Bridge mask-oauth-token returned error`,{providerId:e.providerId,error:t.error}),t??{})).catch(t=>(k.error(`Bridge mask-oauth-token transport failed`,{providerId:e.providerId,error:t instanceof Error?t.message:String(t)}),{}))}async function Ge(e,t,n){let r=s(`/api/secrets/oauth-update`),i=await fetch(r,{method:`POST`,headers:c({"Content-Type":`application/json`}),body:JSON.stringify({providerId:e,accessToken:t,domains:n})});if(!i.ok)return k.warn(`OAuth replica POST non-ok`,{providerId:e,status:i.status,url:r}),{error:`OAuth replica POST HTTP ${i.status}`};let a=await i.json();if(typeof a.maskedValue!=`string`)return k.warn(`OAuth replica POST ok but missing maskedValue`,{providerId:e,url:r}),{error:`OAuth replica POST ok but missing maskedValue`};if(!R(a.maskedValue,t))return k.error(`OAuth mask replica equals the access token; refusing to persist`,{providerId:e}),{error:`mask replica equals the access token`};let o=A(),l=Ve(e,t,a.maskedValue,o);return l.maskedValue&&await j(o),l}async function Ke(e,t){let n=await qe(e,t);if(n.error!==L)return n;let r=A().find(t=>t.providerId===e)?.accessToken;return!r||r===t?n:qe(e,r)}async function qe(e,t){let n=He(e);if(n.length===0)return{error:`no oauth token domains configured`};let r=l();try{return r===`extension-direct`?z({providerId:e,accessToken:t,domains:n},{sendMaskRequest:Ue,getAccounts:A,saveAccounts:j}):r===`extension-delegate`?z({providerId:e,accessToken:t,domains:n},{sendMaskRequest:We,getAccounts:A,saveAccounts:j}):r===`connect`?{error:`no replica store in connect mode`}:await Ge(e,t,n)}catch(t){let n=t instanceof Error?t.message:String(t);return k.error(`OAuth replica sync failed`,{providerId:e,topology:r,error:n}),{error:n}}}async function Je(e){let t=A().find(t=>t.providerId===e);return t?.accessToken?R(t.maskedValue,t.accessToken)?{maskedValue:t.maskedValue}:Ke(e,t.accessToken):{error:`no access token held`}}function Ye(e,t){if(e.accessToken)return`scopes`in e?e.scopes:t?.scopes}async function Xe(e){let t=A().find(t=>t.providerId===e.providerId),n=A().filter(t=>t.providerId!==e.providerId);n.push({providerId:e.providerId,apiKey:``,accessToken:e.accessToken,refreshToken:e.refreshToken,tokenExpiresAt:e.tokenExpiresAt,userName:e.userName,userAvatar:e.userAvatar,baseUrl:e.baseUrl??t?.baseUrl,scopes:Ye(e,t)}),await j(n),await Ke(e.providerId,e.accessToken)}function B(e){let t=A().find(t=>t.providerId===e);return t&&(t.accessToken||t.apiKey)||null}function V(e){let t=A().find(t=>t.providerId===e);return t?t.accessToken||t.apiKey||(E(e).optionalApiKey?`local`:null):null}function H(e){return A().find(t=>t.providerId===e)?.baseUrl??null}function Ze(e){return A().find(t=>t.providerId===e)?.deployment??null}function Qe(e){return A().find(t=>t.providerId===e)?.apiVersion??null}function U(){let e=localStorage.getItem(x)||``,t=e.indexOf(`:`);return t>=0?e.slice(t+1):e}function $e(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t),r=new Set([...i(),...w()]),a=n.includes(`.`);if(r.has(n)||!a){localStorage.setItem(x,e);return}}let n=W();localStorage.setItem(x,`${n}:${e}`)}function et(){return localStorage.getItem(x)||``}function W(){let e=et(),t=e.indexOf(`:`);if(t>0)return e.slice(0,t);let n=A(),r=U();if(r){let e=n.find(e=>T(e.providerId)&&Ee(e.providerId,r));if(e)return e.providerId}let i=n.find(e=>T(e.providerId));return i?i.providerId:n.length>0?n[0].providerId:`anthropic`}function tt(e){let t=U();localStorage.setItem(x,`${e}:${t}`)}function nt(){let e=U();localStorage.setItem(x,e)}function rt(){return V(W())}function it(e){let t=W();M(t,e,H(t)??void 0)}async function at(){await P(W())}function ot(){return H(W())}function st(e){let t=W(),n=B(t);n&&M(t,n,e||void 0)}function ct(){let e=W(),t=B(e);t&&M(e,t)}function lt(){let e=A(),t=W(),n=U();return e.map(e=>{let r={providerId:e.providerId,apiKey:e.apiKey};return e.baseUrl&&(r.baseUrl=e.baseUrl),e.providerId===t&&n&&(r.model=n),r})}async function ut(){let e=A();await Promise.allSettled(e.map(e=>P(e.providerId))),localStorage.removeItem(b),localStorage.removeItem(x);for(let e of be)localStorage.removeItem(e)}function dt(e){return/^(?:gpt[-.]?|o[0-9]|chatgpt)/i.test(e)?`openai`:`anthropic`}function G(e,t,n,r){return{id:t,name:t,provider:e,api:r??`${e}-${dt(t)}`,baseUrl:n??``,contextWindow:2e5,maxTokens:16384,input:[`text`,`image`],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},reasoning:!0}}function K(e,t){return t.isOAuth||e===`azure-ai-foundry`?`anthropic`:e===`bedrock-camp`?`amazon-bedrock`:e}function ft(){let e=new Set;for(let t of A())t.loggedOut||(e.add(t.providerId),e.add(K(t.providerId,E(t.providerId))));return[...e]}function pt(e,t,n){let r=O(e).find(e=>e.id===t);return r?n?{...r,baseUrl:n}:r:null}function mt(e,t,n,r){return n.isOAuth?O(t).find(e=>e.id===r)||{...e,api:`${t}-anthropic`,provider:t}:t===`bedrock-camp`?{...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`}:e}function ht(e,t,n,r,i){let a=pt(e,n,r);if(a)return a;if(t.isOAuth)return G(e,n,r);if(i)throw Error(`Model ${n} is not available from provider ${e}`);return J()}function q(e,t){if(!e)return J();let n=t??W(),r=H(n),i=E(n);if(t!==void 0){let t=pt(n,e,r);if(t)return t}try{let t=K(n,i),a=v(t,e);if(!a?.id)throw Error(`Model ${e} not found`);let o=mt(a,n,i,e);return r?{...o,baseUrl:r}:o}catch(a){return k.debug(`resolveModelById: pi-ai lookup miss, using provider fallback`,{providerId:n,modelId:e,error:a instanceof Error?a.message:String(a)}),ht(n,i,e,r,t!==void 0)}}function gt(e,t){return e.provider===t||e.provider===K(t,E(t))}function J(){let e=W(),t=U(),n=H(e),r=O(e),i=E(e),a=i.defaultModelId?r.find(e=>e.id.toLowerCase().includes(i.defaultModelId.toLowerCase()))?.id:void 0,o=t||a||r[0]?.id||`claude-sonnet-4-6`;try{let t=K(e,i),a=v(t,o);if(!a?.id)throw Error(`Model ${o} not found in ${t} registry`);let s=a;return i.isOAuth?s=r.find(e=>e.id===o)||{...s,api:`${e}-anthropic`,provider:e}:e===`bedrock-camp`&&(s={...s,api:`bedrock-camp-converse`,provider:`bedrock-camp`}),n&&(s={...s,baseUrl:n}),s}catch(t){k.debug(`resolveCurrentModel: pi-ai lookup miss, using provider fallback`,{providerId:e,effectiveModelId:o,error:t instanceof Error?t.message:String(t)});let a=r.find(e=>e.id===o);return a?n?{...a,baseUrl:n}:a:i.isOAuth?G(e,o,n):v(`anthropic`,`claude-sonnet-4-0`)}}function _t(e){let t=e.toLowerCase();if(!t)return null;let n=null;try{n=W()}catch{}if(n!==null){let e=Y(t,[n]);if(e!==null)return e}return Y(t,A().map(e=>e.providerId))}function Y(e,t){let n=null,r=-1;for(let i of t)for(let t of O(i)){let i=t.id.toLowerCase(),a=(t.name??``).toLowerCase();if(!i.includes(e)&&!a.includes(e))continue;let o=t.contextWindow??0;(o>r||o===r&&Et(t.id,n??``)>0)&&(r=o,n=t.id)}return n}function X(){let e=[];try{for(let t of A())e.includes(t.providerId)||e.push(t.providerId)}catch{}try{let t=W();e.includes(t)||e.push(t)}catch{}return e}function vt(e){let t=e.indexOf(`:`);if(t<=0||t===e.length-1)return null;let n=e.slice(0,t),r=e.slice(t+1),i=X().includes(n);if(i)return{providerId:n,modelId:r,configured:i};let a=!1;try{a=w().includes(n)}catch{}return a?{providerId:n,modelId:r,configured:!1}:null}function Z(e,t,n=!0){let r=[e],i=n?Y(e.toLowerCase(),[t]):null;i!==null&&i!==e&&r.push(i);let a=[];try{a=O(t)}catch{}for(let n of r)if(!(a.length>0&&!a.some(e=>e.id===n)))try{if(q(n,t).id===n)return{modelId:n,providerId:t}}catch(r){k.debug(`selectModelFromProvider: candidate did not resolve`,{input:e,candidate:n,providerId:t,error:r instanceof Error?r.message:String(r)})}return null}function yt(e){return e.map(e=>`${e.providerId}:${e.modelId}`).join(`, `)}function Q(e){if(!e)return{ok:!1,error:`unknown model: (empty)`};let t=vt(e);if(t)return Ct(e,t);let n=null;try{n=W()}catch{}if(n!==null){let t=Z(e,n);if(t)return St(t)}return wt(e,n)}function $(){try{return W()}catch{return null}}function bt(e){let t=$();return t===null||ge(_(),t,e.providerId,e.modelId)}function xt(e){let t=$()??`<none>`;return{ok:!1,error:`model not allowed: ${`${e.providerId}:${e.modelId}`} is blocked by ${p} while "${t}" is selected — ${ve(t,e.providerId,e.modelId)}`}}function St(e){return bt(e)?{ok:!0,selection:e}:xt(e)}function Ct(e,t){if(!t.configured){let n=X();return{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" is not configured${n.length>0?`; configured: ${n.join(`, `)}`:``})`}}let n=Z(t.modelId,t.providerId);return n?St(n):{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" does not offer "${t.modelId}")`}}function wt(e,t){let n=X().filter(e=>e!==t);for(let t of[`exact`,`shorthand`]){let r=[];for(let i of n){let n=t===`exact`?e:Y(e.toLowerCase(),[i]);if(n===null)continue;let a=Z(n,i,!1);a&&r.push(a)}let i=r.filter(bt);if(i.length===0&&r.length>0)return xt(r[0]);if(i.length===1)return{ok:!0,selection:i[0]};if(i.length>1)return{ok:!1,error:`ambiguous model: ${e} matches ${yt(i)} — qualify it as provider:model`}}return{ok:!1,error:`unknown model: ${e}`}}function Tt(e){let t=Q(e);return t.ok?t.selection.modelId:null}function Et(e,t){let n=e.match(/\d+/g)?.map(Number)??[],r=t.match(/\d+/g)?.map(Number)??[],i=Math.max(n.length,r.length);for(let e=0;e<i;e++){let t=(n[e]??0)-(r[e]??0);if(t!==0)return t}return 0}export{ue as S,Xe as _,V as a,pe as b,Ze as c,U as d,W as f,Q as g,q as h,rt as i,De as l,J as m,Je as n,Qe as o,gt as p,A as r,H as s,ye as t,O as u,p as v,_e as x,m as y};
|