sliccy 6.154.3 → 6.154.4
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/README.md +1 -0
- package/dist/ui/.vite/manifest.json +175 -175
- package/dist/ui/assets/SKILL-BkCuZ1rT.js +1 -0
- package/dist/ui/assets/{SKILL-Dh6a4Qu2.js → SKILL-C9-SR5fY.js} +1 -1
- package/dist/ui/assets/SKILL-CLF3MdfR.js +1 -0
- package/dist/ui/assets/SKILL-DirmNXBw.js +1 -0
- package/dist/ui/assets/SKILL-UenQ5SPT.js +1 -0
- package/dist/ui/assets/{account-store-BvEgWXDi.js → account-store-Bxl3Ct33.js} +1 -1
- package/dist/ui/assets/account-store-PyDP25J-.js +1 -0
- package/dist/ui/assets/{adobe-Bf44Mdem.js → adobe-CAfItDR6.js} +1 -1
- package/dist/ui/assets/{adobe-DIeHS2Yx.js → adobe-CRHm3ZiT.js} +1 -1
- package/dist/ui/assets/{agent-bridge-pmiSNwoc.js → agent-bridge-BkZinL2A.js} +1 -1
- package/dist/ui/assets/{agent-panels-CWBbnFvW.js → agent-panels-BareeCk8.js} +1 -1
- package/dist/ui/assets/{apply-layout-Eft8a3mq.js → apply-layout-BKbutwOv.js} +1 -1
- package/dist/ui/assets/{apps-BklKPyC6.js → apps-DF-Rc0DX.js} +1 -1
- package/dist/ui/assets/{azure-openai-7VfJn11R.js → azure-openai-CzQsYMy3.js} +1 -1
- package/dist/ui/assets/{azure-openai-o6cG2UDZ.js → azure-openai-DIRfsEOC.js} +1 -1
- package/dist/ui/assets/{browser-q8slJ96R.js → browser-BxGToOGy.js} +1 -1
- package/dist/ui/assets/{budget-usage-source-B5GSAynq.js → budget-usage-source-GIPLIi69.js} +1 -1
- package/dist/ui/assets/build-BRNa03GH.js +3 -0
- package/dist/ui/assets/{cdp-Di9nU0k7.js → cdp-oh0DuXMv.js} +1 -1
- package/dist/ui/assets/{cerebras-BRpdCI1J.js → cerebras-CkeSnL0b.js} +1 -1
- package/dist/ui/assets/{cerebras-DuUgRhfz.js → cerebras-D5U7_320.js} +1 -1
- package/dist/ui/assets/{composer-speech-Chlrvbhg.js → composer-speech-BolqSVFZ.js} +1 -1
- package/dist/ui/assets/{connect-surface-DBXtjfUP.js → connect-surface-BCxPzS-D.js} +1 -1
- package/dist/ui/assets/core-DP-MJcag.js +36 -0
- package/dist/ui/assets/{cost-command-B5aRQX4l.js → cost-command-BzdZ_evm.js} +1 -1
- package/dist/ui/assets/{directed-approval-C2OTs8pv.js → directed-approval-CDx8wAWY.js} +1 -1
- package/dist/ui/assets/edit-BnFODSzt.js +16 -0
- package/dist/ui/assets/{fs-T4T9WqhX.js → fs-C6NHHf6r.js} +1 -1
- package/dist/ui/assets/{fs-Dqh17T5J.js → fs-Y1yRx901.js} +2 -2
- package/dist/ui/assets/{github-CL_7yoHL.js → github-CtPwAeAW.js} +1 -1
- package/dist/ui/assets/{github-auth-A6Mopmg8.js → github-auth-Dg16dadU.js} +1 -1
- package/dist/ui/assets/{github-copilot-B2ugBoRh.js → github-copilot-0h7DYsw7.js} +1 -1
- package/dist/ui/assets/{github-copilot-_5qoxGCT.js → github-copilot-D0Qj8vbq.js} +1 -1
- package/dist/ui/assets/{github-install-8yCqacop.js → github-install-Cljpa_4o.js} +1 -1
- package/dist/ui/assets/{github-koUNqi7Y.js → github-pYZHpBjH.js} +2 -2
- package/dist/ui/assets/{github-zip-CV41OIqx.js → github-zip-DUzMK_AA.js} +1 -1
- package/dist/ui/assets/{guest-tool-gate-_NuvhpkB.js → guest-tool-gate-Mu-876Hm.js} +1 -1
- package/dist/ui/assets/{hear-DScAQlnK.js → hear-BF4j70ha.js} +1 -1
- package/dist/ui/assets/{help-BvOxNE_5.js → help-BJWRLfrH.js} +1 -1
- package/dist/ui/assets/{js-realm-worker-fE5Mi5F2.js → js-realm-worker-Ci2I4buf.js} +1 -1
- package/dist/ui/assets/{kernel-worker-DURzH7I0.js → kernel-worker-BI_Lfm6N.js} +648 -647
- package/dist/ui/assets/{libesm-CSMdpGMF.js → libesm-CTQYxJIv.js} +4 -4
- package/dist/ui/assets/{local-llm-QN3Afs2P.js → local-llm-DXfftbTE.js} +1 -1
- package/dist/ui/assets/{local-llm-D_HQ1XG8.js → local-llm-l6IgHINx.js} +1 -1
- package/dist/ui/assets/{main-DyPgoSsm.js → main-BPi4KXFy.js} +3 -3
- package/dist/ui/assets/{main-cherry-v6ksBa29.js → main-cherry-CGGKAYM7.js} +1 -1
- package/dist/ui/assets/{memory-curation-seam-DXGqoWuF.js → memory-curation-seam-C6YpeprH.js} +1 -1
- package/dist/ui/assets/{mount-CdTXGHvm.js → mount-DEEkB1_d.js} +3 -3
- package/dist/ui/assets/{mount-Drg9K5Dc.js → mount-UVvRQA3s.js} +1 -1
- package/dist/ui/assets/{mount-recovery-CUhmg6UT.js → mount-recovery-RB-9nQ6b.js} +1 -1
- package/dist/ui/assets/{new-session-BBCvIUGf.js → new-session-DP6AUKWc.js} +2 -2
- package/dist/ui/assets/{oauth-bootstrap-37qdAFNO.js → oauth-bootstrap-CMO_7U6r.js} +2 -2
- package/dist/ui/assets/{onboarding-orchestrator-CgzN2Wut.js → onboarding-orchestrator-D9WExyXS.js} +1 -1
- package/dist/ui/assets/{openai-codex-CByg7ExV.js → openai-codex-BoHYzVYq.js} +1 -1
- package/dist/ui/assets/{openai-codex-yA937LOi.js → openai-codex-BsTURzbU.js} +1 -1
- package/dist/ui/assets/{openrouter-Bq9TiHqe.js → openrouter-BHZ95a-Q.js} +1 -1
- package/dist/ui/assets/{openrouter-D1YZxJcz.js → openrouter-CcWbg68Z.js} +1 -1
- package/dist/ui/assets/{openrouter-free-Db1fart9.js → openrouter-free-DBe8IEpx.js} +1 -1
- package/dist/ui/assets/{openrouter-free-BwEyNzqx.js → openrouter-free-DYD76E8s.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-Cj61jTQa.js → openrouter-oauth-Bhoj7wax.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-CFlNuCUA.js → openrouter-oauth-C6B8EJ_v.js} +1 -1
- package/dist/ui/assets/{panel-rpc-handlers-CLsTGXPT.js → panel-rpc-handlers-DuHAIvU2.js} +2 -2
- package/dist/ui/assets/{panelize-shell-Bf9yr7Qf.js → panelize-shell-C6YSK_sm.js} +1 -1
- package/dist/ui/assets/pi-edit-execution-B_MndPan.js +2 -0
- package/dist/ui/assets/{provider-BD-0pPN8.js → provider-BYvMHe3u.js} +1 -1
- package/dist/ui/assets/{provider-C66yENxs.js → provider-KHicY8aQ.js} +2 -2
- package/dist/ui/assets/provider-settings-B_INKQmY.js +1 -0
- package/dist/ui/assets/provider-store-access-CpS-ZdIQ.js +1 -0
- package/dist/ui/assets/provider-store-access-Dcfs6S0T.js +1 -0
- package/dist/ui/assets/{providers-BtR_Oc-m.js → providers-CA98NVBe.js} +2 -2
- package/dist/ui/assets/{providers-C2bj3jKv.js → providers-D2HM3Gid.js} +1 -1
- package/dist/ui/assets/{quick-llm-D-nM71kO.js → quick-llm-B0DrZFep.js} +1 -1
- package/dist/ui/assets/{recovery-screen-YuMG99R0.js → recovery-screen-D2XqB9Ro.js} +1 -1
- package/dist/ui/assets/{remote-terminal-view-DJrkDTkY.js → remote-terminal-view-BXjdqm7-.js} +1 -1
- package/dist/ui/assets/{run-BzyFrWme.js → run-CgQOlMuy.js} +1 -1
- package/dist/ui/assets/{run-D5LoTYwZ.js → run-DS8rwgYd.js} +1 -1
- package/dist/ui/assets/{run-CdzvBpS0.js → run-QKhssIaN.js} +2 -2
- package/dist/ui/assets/{run-C33VP7kC.js → run-XqJwbPb4.js} +1 -1
- package/dist/ui/assets/{run-CDicdmnK.js → run-uRBs2iqf.js} +1 -1
- package/dist/ui/assets/{session-jsonl-By2mMQqb.js → session-jsonl-Dd3hp32C.js} +1 -1
- package/dist/ui/assets/{setup-feature-flags-remote-BuBwyibG.js → setup-feature-flags-remote-C16m2kl3.js} +1 -1
- package/dist/ui/assets/sidecar-repair-Cnj7auqc.js +1 -0
- package/dist/ui/assets/{silent-renew-backoff-CS_htRXY.js → silent-renew-backoff-CR1MteNj.js} +1 -1
- package/dist/ui/assets/{skills-Cg34jB8I.js → skills-BV3xJ5gw.js} +1 -1
- package/dist/ui/assets/slicc-version-BTtIW1Bi.js +1 -0
- package/dist/ui/assets/{snapshot-B6SF0dpr.js → snapshot-BX3eDGyH.js} +1 -1
- package/dist/ui/assets/{snapshot-features-A-jmNbR9.js → snapshot-features-DIRCfL-7.js} +1 -1
- package/dist/ui/assets/{soundscape-smt9sQsm.js → soundscape-xBpF7_kD.js} +1 -1
- package/dist/ui/assets/{speak-DeYQY3R8.js → speak-DWOSewc6.js} +1 -1
- package/dist/ui/assets/{sprinkle-manager-q3Ku7Zvz.js → sprinkle-manager-BXdHl8Cy.js} +1 -1
- package/dist/ui/assets/{sprinkle-renderer-B9Re-_rl.js → sprinkle-renderer-BV1Wttcv.js} +3 -3
- package/dist/ui/assets/{state-NPdZC8rd.js → state-CR5ZXLCN.js} +1 -1
- package/dist/ui/assets/{store-BfdKxUK3.js → store-BCiPgely.js} +1 -1
- package/dist/ui/assets/{store-BbRPb7Di.js → store-DmFH0I9X.js} +1 -1
- package/dist/ui/assets/{style-guide-DKvWxDlM.js → style-guide-BvFX24DM.js} +1 -1
- package/dist/ui/assets/{sudo-CtUlj7iw.js → sudo-B-EkcpfB.js} +1 -1
- package/dist/ui/assets/{sync-dialog-CXgM1Uvh.js → sync-dialog-B2cdLL8q.js} +1 -1
- package/dist/ui/assets/{transformers-env-C7dJtjmn.js → transformers-env-IYiDhpM2.js} +1 -1
- package/dist/ui/assets/{tray-sidecar-BtFzGslJ.js → tray-sidecar-8jNn5UUK.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-BWpOvoVx.js → upgrade-detection-qgLGc4Q4.js} +1 -1
- package/dist/ui/assets/{upskill-lick-skip-ZGNBIEXS.js → upskill-lick-skip-BHVUbSqb.js} +1 -1
- package/dist/ui/assets/usb-claim-broker-C5dFP5bv.js +1 -0
- package/dist/ui/assets/usb-claim-broker-Dacw2Z3M.js +1 -0
- package/dist/ui/assets/usb-operations-v1mGAdRY.js +2 -0
- package/dist/ui/assets/{voice-reply-CCkglCEp.js → voice-reply-QW571MYf.js} +1 -1
- package/dist/ui/assets/{wc-attach-C8CgBet1.js → wc-attach-CIK6FFd5.js} +2 -2
- package/dist/ui/assets/{wc-extension-CHb4P34s.js → wc-extension-1xyakZ_p.js} +2 -2
- package/dist/ui/assets/{wc-follower-DmIZM7ao.js → wc-follower-DCyVgxBD.js} +3 -3
- package/dist/ui/assets/{wc-follower-oauth-CQgoqG9v.js → wc-follower-oauth-CzgF8pm8.js} +1 -1
- package/dist/ui/assets/{wc-gelatiere-B8JhDjog.js → wc-gelatiere-1ERELEHg.js} +1 -1
- package/dist/ui/assets/{wc-live-gCevk2Vh.js → wc-live-jK4-HvEA.js} +5 -5
- package/dist/ui/assets/{wc-nav-CpHEdfwe.js → wc-nav-BPqbmhpu.js} +2 -2
- package/dist/ui/assets/{wc-onboarding-iwpabkC3.js → wc-onboarding-4JLPcMO_.js} +2 -2
- package/dist/ui/assets/{wc-permissions-BEmYiApA.js → wc-permissions-brfCnfZb.js} +2 -2
- package/dist/ui/assets/{wc-placeholder-Cnl3OQfj.js → wc-placeholder-BeeuY5K4.js} +3 -3
- package/dist/ui/assets/{wc-secret-request-B9oB6kAt.js → wc-secret-request-DzT3vuN3.js} +1 -1
- package/dist/ui/assets/{wc-settings-_0roTDdf.js → wc-settings-BlIoHCrC.js} +3 -3
- package/dist/ui/assets/{wc-shell-DkJPUxGT.js → wc-shell-D5UkYz9w.js} +12 -12
- package/dist/ui/assets/{wc-shortcut-config-CIup4nE8.js → wc-shortcut-config-CycKWNUl.js} +1 -1
- package/dist/ui/assets/{wc-shortcuts-C9SNe-vm.js → wc-shortcuts-8_tK1cDi.js} +1 -1
- package/dist/ui/assets/{wc-sprinkles-C7_z9Yfm.js → wc-sprinkles-BN1_kRTR.js} +2 -2
- package/dist/ui/assets/{wc-transcript-export-DnNvjokD.js → wc-transcript-export-DI6b8U4f.js} +2 -2
- package/dist/ui/assets/{wc-tray-DhWe0-Ie.js → wc-tray-DrDQFzF7.js} +4 -4
- package/dist/ui/assets/welcome-detection-BYGa_S8I.js +1 -0
- package/dist/ui/assets/{whisper-session-CMXLPRdV.js → whisper-session-Cbuk405M.js} +2 -2
- package/dist/ui/assets/write-verification-BOaZdtyk.js +1 -0
- package/dist/ui/assets/{xai-grok-BNRMntfw.js → xai-grok-B5EMU1zd.js} +1 -1
- package/dist/ui/assets/{xai-grok-BiF6I0r6.js → xai-grok-dl1o1zgd.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/packages/webapp/index.html +3 -3
- package/package.json +1 -1
- package/dist/ui/assets/SKILL--NWTI0kb.js +0 -1
- package/dist/ui/assets/SKILL-BPuaCaUr.js +0 -1
- package/dist/ui/assets/SKILL-Cl9PVarg.js +0 -1
- package/dist/ui/assets/SKILL-S80DGuUz.js +0 -1
- package/dist/ui/assets/account-store-BYDUpeqo.js +0 -1
- package/dist/ui/assets/core-C9qgaWgE.js +0 -38
- package/dist/ui/assets/provider-settings-DysstkNf.js +0 -1
- package/dist/ui/assets/provider-store-access-BeX0OGEs.js +0 -1
- package/dist/ui/assets/provider-store-access-Cc45yVjV.js +0 -1
- package/dist/ui/assets/sidecar-repair-DRkuMyU_.js +0 -1
- package/dist/ui/assets/slicc-version-DbT116fI.js +0 -1
- package/dist/ui/assets/usb-claim-broker-CnZXiC75.js +0 -1
- package/dist/ui/assets/usb-claim-broker-Do2ZUpNv.js +0 -1
- package/dist/ui/assets/usb-operations-B1y8RuQp.js +0 -2
- package/dist/ui/assets/welcome-detection-BbyyaOna.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e}from"./wc-onboarding-4JLPcMO_.js";export{e as hasOnboardingFinalLickInHistory};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/speak-
|
|
2
|
-
import{t as e}from"./preload-helper-Czpn1I53.js";import{t}from"./logger-DDBAeTLF.js";import{a as n,n as r,o as i,r as a,t as o}from"./transformers-env-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/speak-DWOSewc6.js","assets/preload-helper-Czpn1I53.js","assets/logger-DDBAeTLF.js","assets/runtime-env-Dlaokqg1.js","assets/speech-assets-bridge-CFYafE89.js","assets/transformers-env-IYiDhpM2.js","assets/wc-shortcuts-8_tK1cDi.js","assets/rolldown-runtime-Dd_uD5pT.js","assets/account-store-Bxl3Ct33.js","assets/claude-model-version-CSATLzrT.js","assets/models-BSkXF4T6.js","assets/diagnostics-DN_idNHx.js","assets/provider-env-CTOcDKrQ.js","assets/__vite-browser-external-5eBdwsFd.js","assets/openrouter.models-B-zmcypy.js","assets/libesm-BypnjKEL.js","assets/base64-DJRlyqZu.js","assets/proxied-fetch-DyBveeCl.js","assets/fetch-body-1l9I58Vk.js","assets/api-endpoint-DGxXvrur.js","assets/float-topology-YtctpJaG.js","assets/secret-topology-DLJBhHOi.js","assets/secrets-bridge-client--VZ3E0yk.js","assets/port-bridge-client-Dn8JtUyP.js","assets/tool-ui-eSQ1g2I1.js","assets/providers-CA98NVBe.js","assets/panel-rpc-B3MSVImO.js","assets/bedrock-camp-compat-BeCSBUrZ.js","assets/icons-D5_bMrnB.js","assets/shared-mBEktoxE.js","assets/mime-types-D0xZwlMO.js","assets/transformers.web-B0gi9Hor.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{t as e}from"./preload-helper-Czpn1I53.js";import{t}from"./logger-DDBAeTLF.js";import{a as n,n as r,o as i,r as a,t as o}from"./transformers-env-IYiDhpM2.js";var s=t(`speech:whisper`),c=null,l=`idle`,u=null,d=new Set;function f(){return l}function p(){return u}function m(e){return e&&d.add(e),c||=(l=`loading`,g().then(e=>(l=`ready`,h(),e),e=>{throw l=`failed`,c=null,s.error(`whisper load failed`,e),e})),c}function h(){e(async()=>{let{kokoroWarmup:e}=await import(`./speak-DWOSewc6.js`);return{kokoroWarmup:e}},__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30])).then(({kokoroWarmup:e})=>e()).catch(e=>s.warn(`kokoro warmup (chained after whisper) failed`,e))}async function g(){let{pipeline:t,env:c}=await e(async()=>{let{pipeline:e,env:t}=await import(`./transformers.web-B0gi9Hor.js`);return{pipeline:e,env:t}},__vite__mapDeps([31,7,1]));r(c),await o(n),await a();let l=i(),f=e=>{if(e?.file){if(e.status===`progress`)l.update(e.file,e.loaded??0,e.total??0);else if(e.status===`done`)l.complete(e.file);else return;u=l.snapshot();for(let e of d)e(u)}},p=async e=>await t(`automatic-speech-recognition`,n,{device:e,dtype:e===`webgpu`?`fp32`:`q8`,progress_callback:f}),m=typeof navigator<`u`&&`gpu`in navigator,h;if(m)try{h=await p(`webgpu`)}catch(e){s.warn(`whisper webgpu init failed; retrying on wasm`,e),h=await p(`wasm`)}else h=await p(`wasm`);return s.info(`whisper ready`,{model:n,device:m?`webgpu`:`wasm`}),{async transcribe(e,t){let n=performance.now(),r=await h(e,{chunk_length_s:30,task:`transcribe`,...t?.language?{language:t.language}:{}}),i=Array.isArray(r)?r.map(e=>e.text??``).join(` `):r.text??``;return s.info(`whisper transcribe`,{elapsedMs:Math.round(performance.now()-n),audioSeconds:Math.round(e.length/160)/100,numThreads:c.backends?.onnx?.wasm?.numThreads,device:m?`webgpu`:`wasm`}),i.trim()}}}var _=16e3;async function v(e){if(typeof AudioContext>`u`||typeof OfflineAudioContext>`u`)throw Error(`audio decoding requires a window/page realm (AudioContext unavailable)`);let t=new AudioContext,n;try{n=await t.decodeAudioData(e.slice(0))}finally{await t.close().catch(()=>{})}let r=Math.max(1,Math.ceil(n.duration*_)),i=new OfflineAudioContext(1,r,_),a=i.createBufferSource();return a.buffer=n,a.connect(i.destination),a.start(),(await i.startRendering()).getChannelData(0).slice(0)}var y=t(`speech:whisper-session`),b=2e3,x=500,S=5e3,C=3e4,w=(e,t,n)=>new Promise((r,i)=>{let a=setTimeout(()=>i(Error(`${n} timed out after ${t}ms`)),t);e.then(e=>{clearTimeout(a),r(e)},e=>{clearTimeout(a),i(e)})});function T(e){return e?.split(`-`)[0]?.toLowerCase()||void 0}async function E(e,t){let n;if(t.stream)n=t.stream;else{if(!navigator.mediaDevices?.getUserMedia)throw Error(`microphone capture unavailable in this realm`);n=await navigator.mediaDevices.getUserMedia({audio:t.deviceId&&t.deviceId!=="default"?{deviceId:{exact:t.deviceId}}:!0})}let r=new MediaRecorder(n),i=[],a=T(t.lang),o=!1,s=!1;r.ondataavailable=e=>{e.data.size>0&&i.push(e.data)};let c=async()=>{if(i.length===0)return``;let t=await v(await new Blob(i,{type:r.mimeType||`audio/webm`}).arrayBuffer());return e.transcribe(t,{language:a})},l=Promise.resolve(``),u=()=>{let e=l.catch(()=>``).then(()=>c());return l=e.catch(()=>``),e},d=setInterval(()=>{o||s||i.length===0||(o=!0,u().then(e=>{!s&&e&&t.onPartial?.(e)}).catch(e=>{y.warn(`partial transcription failed`,e)}).finally(()=>{o=!1}))},b),f=()=>{if(clearInterval(d),r.state!==`inactive`)try{r.stop()}catch{}for(let e of n.getTracks())e.stop()},p=()=>new Promise(e=>{if(r.state===`inactive`){e();return}r.onstop=()=>e();try{r.stop()}catch{e()}});return r.start(x),{async stop(){if(s)return``;s=!0,clearInterval(d);try{await w(p(),S,`recorder flush`);for(let e of n.getTracks())e.stop();return await w(u(),C,`transcription`)}catch(e){for(let e of n.getTracks())e.stop();let r=e instanceof Error?e.message:String(e);return t.onError?.(`transcription failed: ${r}`),``}},cancel(){s||(s=!0,f())}}}export{p as a,m as i,T as n,f as o,v as r,E as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e}from"./path-utils-DNJG-zQf.js";import{r as t}from"./virtual-device-paths-CS5o4QVz.js";async function n(n,r,i){if(t(e(r)))return null;try{let e=await n.readTextFile(r);if(i.length<=262144)return e===i?null:`Write did not land: ${r} content mismatch (expected ${i.length} chars, got ${e.length})`;if(e.length!==i.length)return`Write did not land: ${r} content mismatch (expected ${i.length} chars, got ${e.length})`;let t=4096;return e.slice(0,t)!==i.slice(0,t)||e.slice(-4096)!==i.slice(-4096)?`Write did not land: ${r} content mismatch (head/tail sample)`:null}catch(e){return`Write did not land: ${r} is not readable (${e instanceof Error?e.message:String(e)})`}}export{n as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{G as e,f as t}from"./account-store-
|
|
1
|
+
import{G as e,f as t}from"./account-store-Bxl3Ct33.js";import{a as n}from"./diagnostics-DN_idNHx.js";import{d as r,l as i}from"./claude-model-version-CSATLzrT.js";import{c as a,i as o,r as s,s as c}from"./legacy-api-aliases-bAJ0Klzq.js";import"./main-BPi4KXFy.js";import{n as l,r as u,t as d}from"./pkce-hGf_TEnP.js";import{XaiErrorCode as f,XaiOAuthError as p}from"./xai-grok-errors-bnlIBeu-.js";var m=`https://cli-chat-proxy.grok.com/v1/billing?format=credits`,h=8e3,g=65536,_=1e3;function v(e){return typeof e==`object`&&!!e}function y(e){if(!v(e))return null;let t=v(e.config)?e.config:e,n=t.creditUsagePercent;if(typeof n!=`number`||!Number.isFinite(n))return null;let r=Math.min(_,Math.max(0,n)),i=v(t.currentPeriod)?t.currentPeriod:{},a=i.type,o=typeof a==`string`?a.replace(`USAGE_PERIOD_TYPE_`,``).toLowerCase():``,s=i.end??t.billingPeriodEnd,c=typeof s==`string`&&Number.isFinite(Date.parse(s))?s:void 0;return{percent:r,status:`ok`,window:o&&o!==`unspecified`?o:`billing`,...c?{resetsAt:c}:{}}}async function b(e,t,n={}){let r=new AbortController,i=setTimeout(()=>r.abort(),n.timeoutMs??h);try{let n=await t(m,{headers:{Authorization:`Bearer ${e}`},signal:r.signal}),{status:i}=n;if(i===404||i===501)return null;if(!n.ok)throw Error(`xAI Grok usage: ${i===401||i===403?`re-login required`:`failed`} (${i})`);let a=await n.text();if(a.length>g)return null;try{return y(JSON.parse(a))}catch{return null}}finally{clearTimeout(i)}}var x=`xai-grok`,S=`https://auth.x.ai`,C=`${S}/oauth2/authorize`,w=`${S}/oauth2/token`,T=`b1a00492-073a-47ea-816f-4c329264a828`,E=`openid profile email offline_access grok-cli:access api:access`,D=`http://127.0.0.1:56121/callback`,O=`http://127.0.0.1:56121/*`,k=`https://api.x.ai/v1`,A=`grok-4.5`,j=`${x}-openai`;function M(){return i(`xai`)}function N(e){return{id:e.id,name:e.name,api:`openai`,reasoning:e.reasoning,input:e.input,context_window:e.contextWindow,max_tokens:e.maxTokens,compat:e.compat,thinkingLevelMap:e.thinkingLevelMap}}function P(e){let t=M(),n=t.find(t=>t.id===e.id);if(!n&&(console.warn(`xAI model "${e.id}" is no longer in the pi-ai catalog; falling back to default "${A}"`),n=t.find(e=>e.id===A),!n))throw Error(`xAI default model "${A}" is not registered by pi-ai`);if(n.api!==`openai-responses`&&n.api!==`openai-completions`)throw Error(`Unsupported pi-ai API "${n.api}" for xAI model "${e.id}"`);return{...e,...n,baseUrl:k}}async function F(e,t){let n=new URLSearchParams({grant_type:`authorization_code`,code:e,redirect_uri:D,client_id:T,code_verifier:t}),r=await fetch(w,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:n});if(!r.ok)throw new p(`xAI token exchange failed: ${r.status} ${await r.text()}`,f.TOKEN_EXCHANGE_FAILED);let i=await r.json();if(!i.access_token)throw new p(`xAI token exchange did not return access_token.`,f.TOKEN_EXCHANGE_INVALID);return i}async function I(e){try{let t=new URLSearchParams({grant_type:`refresh_token`,refresh_token:e,client_id:T}),n=await fetch(w,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:t});return n.ok?await n.json():(console.error(`[xai-grok] refresh failed:`,n.status,await n.text()),null)}catch(e){return console.error(`[xai-grok] refresh error:`,e instanceof Error?e.message:String(e)),null}}function L(){return t().find(e=>e.providerId===x)}async function R(){let t=L();if(!t?.accessToken)throw new p("Not signed in to xAI Grok — run /login or `oauth-token xai-grok`",f.AUTH_MISSING,!0);let n=t.tokenExpiresAt??0;if(n&&Date.now()+6e4<n)return t.accessToken;if(t.refreshToken){let n=await I(t.refreshToken);if(n?.access_token)return await e({providerId:x,accessToken:n.access_token,refreshToken:n.refresh_token??t.refreshToken,tokenExpiresAt:Date.now()+(n.expires_in??21600)*1e3,scopes:n.scope??t.scopes}),n.access_token}return t.accessToken}async function z(){if(!L()?.accessToken)throw Error(`xAI Grok budget: not signed in`);return b(await R(),fetch)}function B(e,t){return{type:`error`,reason:`error`,error:{role:`assistant`,content:[],api:j,provider:x,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:`error`,errorMessage:t instanceof Error?t.message:String(t),timestamp:Date.now()}}}function V(e,t){return t?{...e??{},"x-grok-conv-id":t}:e}function H(e){console.error(`[xai-grok] Stream error:`,e instanceof Error?e.message:String(e))}async function U(e,t,n,r){try{let i=await R(),a=r.sessionId,c=P(t),l={...r,apiKey:i,headers:V(r.headers,a)},u=c.api===`openai-responses`?o(c,n,l):s(c,n,l);for await(let t of u)e.push(t);e.end()}catch(n){H(n),e.push(B(t,n)),e.end()}}async function W(e,t,n,r){try{let i=await R(),o=r?.sessionId,s=P(t),l={...r,apiKey:i,headers:V(r?.headers,o)},u=s.api===`openai-responses`?a(s,n,l):c(s,n,l);for await(let t of u)e.push(t);e.end()}catch(n){H(n),e.push(B(t,n)),e.end()}}var G=(e,t,r={})=>{let i=n();return U(i,e,t,r).catch(H),i},K=(e,t,r)=>{let i=n();return W(i,e,t,r).catch(H),i},q={id:x,name:`xAI Grok (SuperGrok OAuth)`,description:`Grok via xAI OAuth — uses your SuperGrok subscription, no API key needed. Default model is Grok 4.5.`,requiresApiKey:!1,requiresBaseUrl:!1,isOAuth:!0,defaultModelId:A,oauthTokenDomains:[`api.x.ai`,`*.x.ai`,`auth.x.ai`,`accounts.x.ai`,`cli-chat-proxy.grok.com`],getModelIds:()=>M().map(N),getBudgetUsage:z,onOAuthLoginIntercepted:async(t,n,r)=>{let i=l(),a=await d(i),o=u(),s=u(),c=new URL(C);c.searchParams.set(`response_type`,`code`),c.searchParams.set(`client_id`,T),c.searchParams.set(`redirect_uri`,D),c.searchParams.set(`scope`,r?.scopes??E),c.searchParams.set(`code_challenge`,a),c.searchParams.set(`code_challenge_method`,`S256`),c.searchParams.set(`state`,o),c.searchParams.set(`nonce`,s),c.searchParams.set(`plan`,`generic`),c.searchParams.set(`referrer`,`slicc`);let m=await t({authorizeUrl:c.toString(),redirectUriPattern:O,onCapture:`close`});if(!m)throw new p(`xAI OAuth login was cancelled or timed out`,f.CALLBACK_TIMEOUT);let h=new URL(m),g=h.searchParams.get(`code`),_=h.searchParams.get(`state`);if(!g)throw new p(`xAI OAuth redirect did not include a code`,f.CODE_MISSING);if(_!==o)throw new p(`xAI OAuth state mismatch — possible CSRF, aborting`,f.STATE_MISMATCH);let v=await F(g,i);await e({providerId:x,accessToken:v.access_token,refreshToken:v.refresh_token,tokenExpiresAt:Date.now()+(v.expires_in??21600)*1e3,baseUrl:k,scopes:v.scope}),n()},onOAuthLogout:async()=>{await e({providerId:x,accessToken:``})},onSilentRenew:async()=>{let t=L();if(!t?.refreshToken)return null;let n=await I(t.refreshToken);return n?.access_token?(await e({providerId:x,accessToken:n.access_token,refreshToken:n.refresh_token??t.refreshToken,tokenExpiresAt:Date.now()+(n.expires_in??21600)*1e3,scopes:n.scope??t.scopes}),n.access_token):null}};function J(){r({api:j,stream:G,streamSimple:K})}export{f as XaiErrorCode,p as XaiOAuthError,q as config,J as register};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as e,r as t}from"./account-store-
|
|
1
|
+
import{_ as e,r as t}from"./account-store-PyDP25J-.js";import{a as n}from"./diagnostics-DN_idNHx.js";import{a as r,r as i}from"./compat-uEPf6-XW.js";import{c as a,i as o,r as s,s as c}from"./legacy-api-aliases-jKEvBgOL.js";import"./provider-settings-B_INKQmY.js";import{n as l,r as u,t as d}from"./pkce-hGf_TEnP.js";import{XaiErrorCode as f,XaiOAuthError as p}from"./xai-grok-errors-ByEMMSoz.js";function m(e){return typeof e==`object`&&!!e}function h(e){if(!m(e))return null;let t=m(e.config)?e.config:e,n=t.creditUsagePercent;if(typeof n!=`number`||!Number.isFinite(n))return null;let r=Math.min(1e3,Math.max(0,n)),i=m(t.currentPeriod)?t.currentPeriod:{},a=i.type,o=typeof a==`string`?a.replace(`USAGE_PERIOD_TYPE_`,``).toLowerCase():``,s=i.end??t.billingPeriodEnd,c=typeof s==`string`&&Number.isFinite(Date.parse(s))?s:void 0;return{percent:r,status:`ok`,window:o&&o!==`unspecified`?o:`billing`,...c?{resetsAt:c}:{}}}async function g(e,t,n={}){let r=new AbortController,i=setTimeout(()=>r.abort(),n.timeoutMs??8e3);try{let n=await t(`https://cli-chat-proxy.grok.com/v1/billing?format=credits`,{headers:{Authorization:`Bearer ${e}`},signal:r.signal}),{status:i}=n;if(i===404||i===501)return null;if(!n.ok)throw Error(`xAI Grok usage: ${i===401||i===403?`re-login required`:`failed`} (${i})`);let a=await n.text();if(a.length>65536)return null;try{return h(JSON.parse(a))}catch{return null}}finally{clearTimeout(i)}}const _=`xai-grok`,v=`https://auth.x.ai`,y=`${v}/oauth2/authorize`,b=`${v}/oauth2/token`,x=`b1a00492-073a-47ea-816f-4c329264a828`,S=`http://127.0.0.1:56121/callback`,C=`https://api.x.ai/v1`,w=`grok-4.5`,T=`${_}-openai`;function E(){return i(`xai`)}function D(e){return{id:e.id,name:e.name,api:`openai`,reasoning:e.reasoning,input:e.input,context_window:e.contextWindow,max_tokens:e.maxTokens,compat:e.compat,thinkingLevelMap:e.thinkingLevelMap}}function O(e){let t=E(),n=t.find(t=>t.id===e.id);if(!n&&(console.warn(`xAI model "${e.id}" is no longer in the pi-ai catalog; falling back to default "${w}"`),n=t.find(e=>e.id===w),!n))throw Error(`xAI default model "${w}" is not registered by pi-ai`);if(n.api!==`openai-responses`&&n.api!==`openai-completions`)throw Error(`Unsupported pi-ai API "${n.api}" for xAI model "${e.id}"`);return{...e,...n,baseUrl:C}}async function k(e,t){let n=new URLSearchParams({grant_type:`authorization_code`,code:e,redirect_uri:S,client_id:x,code_verifier:t}),r=await fetch(b,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:n});if(!r.ok)throw new p(`xAI token exchange failed: ${r.status} ${await r.text()}`,f.TOKEN_EXCHANGE_FAILED);let i=await r.json();if(!i.access_token)throw new p(`xAI token exchange did not return access_token.`,f.TOKEN_EXCHANGE_INVALID);return i}async function A(e){try{let t=new URLSearchParams({grant_type:`refresh_token`,refresh_token:e,client_id:x}),n=await fetch(b,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:t});return n.ok?await n.json():(console.error(`[xai-grok] refresh failed:`,n.status,await n.text()),null)}catch(e){return console.error(`[xai-grok] refresh error:`,e instanceof Error?e.message:String(e)),null}}function j(){return t().find(e=>e.providerId===_)}async function M(){let t=j();if(!t?.accessToken)throw new p("Not signed in to xAI Grok — run /login or `oauth-token xai-grok`",f.AUTH_MISSING,!0);let n=t.tokenExpiresAt??0;if(n&&Date.now()+6e4<n)return t.accessToken;if(t.refreshToken){let n=await A(t.refreshToken);if(n?.access_token)return await e({providerId:_,accessToken:n.access_token,refreshToken:n.refresh_token??t.refreshToken,tokenExpiresAt:Date.now()+(n.expires_in??21600)*1e3,scopes:n.scope??t.scopes}),n.access_token}return t.accessToken}async function N(){if(!j()?.accessToken)throw Error(`xAI Grok budget: not signed in`);return g(await M(),fetch)}function P(e,t){return{type:`error`,reason:`error`,error:{role:`assistant`,content:[],api:T,provider:_,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:`error`,errorMessage:t instanceof Error?t.message:String(t),timestamp:Date.now()}}}function F(e,t){return t?{...e??{},"x-grok-conv-id":t}:e}function I(e){console.error(`[xai-grok] Stream error:`,e instanceof Error?e.message:String(e))}async function L(e,t,n,r){try{let i=await M(),a=r.sessionId,c=O(t),l={...r,apiKey:i,headers:F(r.headers,a)},u=c.api===`openai-responses`?o(c,n,l):s(c,n,l);for await(let t of u)e.push(t);e.end()}catch(n){I(n),e.push(P(t,n)),e.end()}}async function R(e,t,n,r){try{let i=await M(),o=r?.sessionId,s=O(t),l={...r,apiKey:i,headers:F(r?.headers,o)},u=s.api===`openai-responses`?a(s,n,l):c(s,n,l);for await(let t of u)e.push(t);e.end()}catch(n){I(n),e.push(P(t,n)),e.end()}}const z=(e,t,r={})=>{let i=n();return L(i,e,t,r).catch(I),i},B=(e,t,r)=>{let i=n();return R(i,e,t,r).catch(I),i},V={id:_,name:`xAI Grok (SuperGrok OAuth)`,description:`Grok via xAI OAuth — uses your SuperGrok subscription, no API key needed. Default model is Grok 4.5.`,requiresApiKey:!1,requiresBaseUrl:!1,isOAuth:!0,defaultModelId:w,oauthTokenDomains:[`api.x.ai`,`*.x.ai`,`auth.x.ai`,`accounts.x.ai`,`cli-chat-proxy.grok.com`],getModelIds:()=>E().map(D),getBudgetUsage:N,onOAuthLoginIntercepted:async(t,n,r)=>{let i=l(),a=await d(i),o=u(),s=u(),c=new URL(y);c.searchParams.set(`response_type`,`code`),c.searchParams.set(`client_id`,x),c.searchParams.set(`redirect_uri`,S),c.searchParams.set(`scope`,r?.scopes??`openid profile email offline_access grok-cli:access api:access`),c.searchParams.set(`code_challenge`,a),c.searchParams.set(`code_challenge_method`,`S256`),c.searchParams.set(`state`,o),c.searchParams.set(`nonce`,s),c.searchParams.set(`plan`,`generic`),c.searchParams.set(`referrer`,`slicc`);let m=await t({authorizeUrl:c.toString(),redirectUriPattern:`http://127.0.0.1:56121/*`,onCapture:`close`});if(!m)throw new p(`xAI OAuth login was cancelled or timed out`,f.CALLBACK_TIMEOUT);let h=new URL(m),g=h.searchParams.get(`code`),v=h.searchParams.get(`state`);if(!g)throw new p(`xAI OAuth redirect did not include a code`,f.CODE_MISSING);if(v!==o)throw new p(`xAI OAuth state mismatch — possible CSRF, aborting`,f.STATE_MISMATCH);let b=await k(g,i);await e({providerId:_,accessToken:b.access_token,refreshToken:b.refresh_token,tokenExpiresAt:Date.now()+(b.expires_in??21600)*1e3,baseUrl:C,scopes:b.scope}),n()},onOAuthLogout:async()=>{await e({providerId:_,accessToken:``})},onSilentRenew:async()=>{let t=j();if(!t?.refreshToken)return null;let n=await A(t.refreshToken);return n?.access_token?(await e({providerId:_,accessToken:n.access_token,refreshToken:n.refresh_token??t.refreshToken,tokenExpiresAt:Date.now()+(n.expires_in??21600)*1e3,scopes:n.scope??t.scopes}),n.access_token):null}};function H(){r({api:T,stream:z,streamSimple:B})}export{f as XaiErrorCode,p as XaiOAuthError,V as config,H as register};
|
package/dist/ui/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>slicc</title>
|
|
7
7
|
<link rel="icon" type="image/png" href="/logos/sliccy-color-1scoops-32x32.png" />
|
|
8
|
-
<script type="module" crossorigin src="/assets/main-
|
|
8
|
+
<script type="module" crossorigin src="/assets/main-BPi4KXFy.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Dd_uD5pT.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-P2Xu9kJm.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-Czpn1I53.js">
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
<link rel="modulepreload" crossorigin href="/assets/port-bridge-client-Dn8JtUyP.js">
|
|
28
28
|
<link rel="modulepreload" crossorigin href="/assets/secrets-bridge-client--VZ3E0yk.js">
|
|
29
29
|
<link rel="modulepreload" crossorigin href="/assets/tool-ui-eSQ1g2I1.js">
|
|
30
|
-
<link rel="modulepreload" crossorigin href="/assets/providers-
|
|
30
|
+
<link rel="modulepreload" crossorigin href="/assets/providers-CA98NVBe.js">
|
|
31
31
|
<link rel="modulepreload" crossorigin href="/assets/panel-rpc-B3MSVImO.js">
|
|
32
32
|
<link rel="modulepreload" crossorigin href="/assets/bedrock-camp-compat-BeCSBUrZ.js">
|
|
33
|
-
<link rel="modulepreload" crossorigin href="/assets/account-store-
|
|
33
|
+
<link rel="modulepreload" crossorigin href="/assets/account-store-Bxl3Ct33.js">
|
|
34
34
|
<link rel="modulepreload" crossorigin href="/assets/bridge-protocol-06nbmp2X.js">
|
|
35
35
|
<link rel="modulepreload" crossorigin href="/assets/messages-zAgr7E2T.js">
|
|
36
36
|
<link rel="modulepreload" crossorigin href="/assets/tray-runtime-config-B9jN9kUJ.js">
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>slicc</title>
|
|
7
7
|
<link rel="icon" type="image/png" href="/logos/sliccy-color-1scoops-32x32.png" />
|
|
8
|
-
<script type="module" crossorigin src="/assets/main-
|
|
8
|
+
<script type="module" crossorigin src="/assets/main-BPi4KXFy.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Dd_uD5pT.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-P2Xu9kJm.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-Czpn1I53.js">
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
<link rel="modulepreload" crossorigin href="/assets/port-bridge-client-Dn8JtUyP.js">
|
|
28
28
|
<link rel="modulepreload" crossorigin href="/assets/secrets-bridge-client--VZ3E0yk.js">
|
|
29
29
|
<link rel="modulepreload" crossorigin href="/assets/tool-ui-eSQ1g2I1.js">
|
|
30
|
-
<link rel="modulepreload" crossorigin href="/assets/providers-
|
|
30
|
+
<link rel="modulepreload" crossorigin href="/assets/providers-CA98NVBe.js">
|
|
31
31
|
<link rel="modulepreload" crossorigin href="/assets/panel-rpc-B3MSVImO.js">
|
|
32
32
|
<link rel="modulepreload" crossorigin href="/assets/bedrock-camp-compat-BeCSBUrZ.js">
|
|
33
|
-
<link rel="modulepreload" crossorigin href="/assets/account-store-
|
|
33
|
+
<link rel="modulepreload" crossorigin href="/assets/account-store-Bxl3Ct33.js">
|
|
34
34
|
<link rel="modulepreload" crossorigin href="/assets/bridge-protocol-06nbmp2X.js">
|
|
35
35
|
<link rel="modulepreload" crossorigin href="/assets/messages-zAgr7E2T.js">
|
|
36
36
|
<link rel="modulepreload" crossorigin href="/assets/tray-runtime-config-B9jN9kUJ.js">
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e="---\nname: skill-authoring\ndescription: |\n Use this when the user wants to write a new skill, edit an existing one, or\n understand SLICC's skill system. Covers SKILL.md frontmatter (name,\n description, allowed-tools), how to write a description that triggers\n reliably, native `/workspace/skills/` vs compatibility `.agents/` /\n `.claude/skills/` discovery, and when to ship companion files like `.jsh`\n scripts or `.bsh` browser hooks.\nallowed-tools: bash, read_file, write_file, edit_file\n---\n\n# Skill authoring\n\nA skill is a folder with a `SKILL.md` (and optional companion files) that loads into the agent's system prompt when the description matches the user's intent. This skill is about authoring those folders well.\n\n## Discovery\n\nSLICC discovers five kinds of skill roots:\n\n| Root | Source | Mutability |\n| ------------------------------------------------------------ | -------------------------------------------- | ----------------------------------- |\n| `/workspace/skills/<name>/SKILL.md` | Bundled or installed via `upskill` | Install-managed; you can edit them |\n| `.agents/skills/<name>/SKILL.md` (anywhere) | Compatibility (Cursor / SuperClaude) | Read-only (discovered, not managed) |\n| `.claude/skills/<name>/SKILL.md` (anywhere) | Compatibility (Claude Code) | Read-only (discovered, not managed) |\n| `<mount>/.claude-plugin/marketplace.json` → plugin `skills/` | Claude Code marketplace (mounted repos) | Read-only (discovered, not managed) |\n| `<plugin-root>/skills/<name>/SKILL.md` | Agent Plugins installed via `plugin install` | Read-only (discovered, not managed) |\n\nSkills under `/workspace/skills/` installed with `upskill` carry a `.upskill` provenance record (source, ref, commit, file list) that `upskill list --outdated` consults and `upskill update [--dry-run]` uses to refresh them. `upskill` never modifies or deletes a **dotfile** in a skill directory, so that is where a skill should keep its credentials and local state (`scripts/.config`) — everything else is replaced on update.\n\nThe marketplace root is auto-discovered: when a mounted directory contains `.claude-plugin/marketplace.json`, SLICC reads the manifest, resolves each plugin's `source` path, and discovers skills at `<plugin-source>/skills/<name>/SKILL.md`. No install step needed — mount the repo and the skills are live immediately. Agent Plugins (agent-plugins.org packages with a `plugin.json` manifest) require an explicit `plugin install <path|repo>` — `<repo>` may be a local directory or a GitHub reference (`owner/repo`, `owner/repo@branch`, or a `https://github.com/owner/repo[/tree/branch[/dir]]` URL, downloaded into `/workspace/.plugins/sources/`); their skills then surface automatically. Precedence: native > agents > claude > marketplace > plugin.\n\nWhen you create a new skill **for SLICC**, put it in `/workspace/skills/<name>/`. The `.agents/`, `.claude/`, and marketplace paths exist so SLICC can pick up skills authored for other agents without modification — don't create new skills there.\n\n## SKILL.md structure\n\n```markdown\n---\nname: <slug>\ndescription: |\n Use this when ...\n ... (1–3 sentences explaining trigger conditions, what's covered, and what's\n NOT covered if there's a sibling skill that handles related topics.)\nallowed-tools: bash, read_file, write_file, edit_file\n---\n\n# Title (matches `name`)\n\n... body ...\n```\n\n### Frontmatter fields\n\n- **`name`** — lowercase, kebab-case. Must match the folder name. This is what `skill list` shows.\n- **`description`** — the trigger string. The agent uses this to decide whether to load the skill. Get this right; everything else is secondary.\n- **`allowed-tools`** — comma-separated list of tools the skill needs. Without this, the agent may load the skill but find it can't execute the steps. Common values:\n - `bash` — almost every skill.\n - `read_file, write_file, edit_file` — for skills that author files (sprinkles, config edits, three-way merges).\n - Omit only for purely informational skills.\n\n### Writing a good description\n\nThe description is a trigger, not a summary. It runs through the agent at every turn — too vague and the skill loads when irrelevant; too narrow and it doesn't load when needed.\n\n**Pattern that works**: \"Use this when \\<user-facing trigger\\>. Covers \\<topics\\>. \\[For \\<adjacent topic\\> use \\<sibling skill\\>.\\]\"\n\nCompare:\n\n- ❌ `description: Licks, webhooks, cron tasks, viewing pages/images, screencapture, onboarding`\n Keyword soup. The agent has to guess what \"licks\" or \"screencapture\" mean for this user.\n- ✅ `description: Use this when setting up event-driven automation in SLICC — webhooks, cron tasks, or filesystem watchers that route events to scoops. Covers webhook, crontask, and fswatch. Read this BEFORE wiring anything that should fire on a schedule, an HTTP call, or a VFS change.`\n Names the user intent (\"setting up event-driven automation\"), names the commands the agent will reach for, and tells the agent when to load it.\n\nRules of thumb:\n\n- Lead with **\"Use this when...\"** or **\"Use this whenever...\"**.\n- Name the **user-facing trigger** (what the user said), not just the implementation.\n- If there's a closely-named sibling skill (dips vs sprinkles, mount vs other storage), say which is which inside the description so the agent doesn't load both.\n- Multi-line YAML scalars are fine for longer descriptions — use the `|` block style.\n\n## Body conventions\n\n- Lead with one sentence stating what the skill is. No preamble.\n- Use tables for option matrices (commands × flags, tradeoffs, etc.).\n- Code blocks are bash unless otherwise needed.\n- Include a \"Don't\" or \"Common errors\" section near the end if the skill is failure-prone — the agent will read it before acting.\n- If the skill is large (> ~150 lines), split a reference table or example gallery into a companion `<topic>.md` and have the SKILL.md `read_file` it on demand. The `sprinkles/` skill (style-guide.md) and `dips/` skill (patterns.md) follow this pattern.\n\n## Companion files\n\n### `.jsh` — JavaScript shell scripts\n\n`.jsh` files on the shell's `$PATH` search roots are auto-discovered as shell commands. **Full reference: `./jsh-runtime-extensions.md`.**\n\n- **Auto-discovery**: registered as callable commands by filename (without the extension), from the `$PATH` roots — `/workspace/skills`, `/workspace/.mcp/aliases`, `/workspace/bin`, `/shared/bin` by default. A skill can ship its own commands by including a `.jsh` next to `SKILL.md`. Earlier roots win basename collisions, so `/workspace/skills/` wins. For commands elsewhere, add their dir to the PATH: `echo 'export PATH=\"$PATH:/my/tools\"' >> ~/.profile`.\n- **Direct execution**: run any script by path with `jsh /tmp/tool.jsh [args…]`. `jsh` is an alias for the Node shim, including `sliccy:` module resolution and argument passthrough; `process.argv[0]` remains `node` for Node compatibility.\n- **Dual-mode**: works in both the CLI server and the Chrome extension (sandbox iframe). Don't rely on CLI-only Node modules.\n- **Top-level `await`**: scripts are wrapped in `AsyncFunction`, so `await` at the top level works. Prefer it — errors surface instead of becoming unhandled rejections. Fire-and-forget `.then()`, unawaited `main()`, `setTimeout`, and `await fetch(…).json()` / `.text()` also keep the realm alive: like Node, the process stays up while I/O (fs/exec/fetch, including reading the response body) or timers are outstanding, and `process.exit()` skips the rest. A Promise with no handle (`new Promise(() => {})`) does **not** keep it alive. `node --check` / `-c` syntax-checks without executing (top-level `await` and ESM `import`/`export` are valid — the same entry transpile `node file.mjs` uses). `node --input-type=module` treats `-e` / stdin / `.js` as ESM.\n\n#### Runtime surface (use these — don't reinvent)\n\nNode-standard bare globals:\n\n| Global | Use for |\n| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `process` | `argv`, `env`, `cwd()`, `exit(code)`, `stdout.write`, `stderr.write`; `stdin` is one-shot buffered (no streaming) — `read()`, events, or async iterator, drain once |\n| `console` | `log`/`info`/`debug`/`dirxml`/`table`/`dir` → stdout; `warn`/`error`/`assert`/`trace` → stderr (`assert` does not throw); `group*` indent; `time*`/`count*` labeled; `clear` no-op |\n| `fetch` | Standard `fetch` routed through SLICC's proxied transport (cookies + CORS handled). |\n| `require(p)` | Synchronous CJS `require`. Use `require('sliccy:<name>')` for capability bridges and `require('fs')` for the VFS bridge (see below). |\n| `__dirname` / `__filename` | CJS scope vars — the script's own directory and absolute path. |\n\nCapability bridges via `require('sliccy:<name>')` (full reference: `./jsh-runtime-extensions.md`):\n\n| `require('sliccy:<name>')` | Use for |\n| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `sliccy:exec` | Callable `exec(cmd)` + `.spawn(argv[])`. Composes with any supplemental command or `.jsh` script. |\n| `sliccy:agent` | Callable `agent(prompt, opts?)` → sub-scoop final text (parsed when `schema` set) + `.spawn(...)` → `{ finalText, exitCode, stderr }`. `opts`: `model`, `thinking`, `cwd`, `allowedCommands`, `readOnly`, `schema`. |\n| `sliccy:skill` | `dir` / `root` / `refs` / `assets` / `config()` / `token(providerId)` — skill-root `references/` and `assets/`, script-dir `.config`, provider tokens. |\n| `sliccy:http` | `http.client({ baseUrl, token, headers, retry, timeoutMs })` — standard API-client builder. |\n| `sliccy:browser` | `findTab`, `ensureTab`, `eval`, `evalAsync`, `cookie`, `localStorage`, `fetch`, `websocket.on(...)`. |\n| `sliccy:usb` / `sliccy:serial` / `sliccy:hid` | `list()` / `request()` + device methods. Chromium-only. |\n| `sliccy:cli` | `die(msg, opts?)`, `out(value)`, `warn(msg, opts?)`, `help(text)`. |\n| `sliccy:color` | ANSI helpers (`green`, `red`, `bold`, `dim`, …) auto-disabled on non-TTY / `NO_COLOR`. |\n| `sliccy:time` | `parseDuration`, `ago`, `range`, `future`, `gmailDate`. |\n| `sliccy:fmt` | `trunc`, `col`, `table`, `date`. |\n| `sliccy:pool` | `pool(n, items, fn)` — bounded concurrency runner. |\n\nVFS bridge:\n\n| `require('fs')` / `require('node:fs')` | `readFile`, `writeFile`, `readFileBinary`, `writeFileBinary`, `readDir`, `exists`, `stat`, `mkdir`, `rm`, `fetchToFile` — all paths are VFS, async. There is no bare `fs` global. |\n| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n\n#### Runtime extensions (live — prefer these over hand-rolled equivalents)\n\nReach these via `require('sliccy:<name>')`. Full reference: `./jsh-runtime-extensions.md`. Use them instead of reimplementing the cross-skill patterns they replace.\n\n- **`process.argv.parseFlags()`** — returns `{ positional, flags, subcommand }`. Replaces the per-skill `--flag=val` / `--flag val` parsing loop.\n- **`require('sliccy:browser')`** — `findTab({ domain | urlMatch })`, `ensureTab(url)`, `eval(tab, fn)`, `evalAsync(tab, fn)`, `cookie(tab, name)`, `localStorage(tab, key)`. Replaces shelling out to `playwright-cli tab-list` and regex-parsing its output.\n- **`browser.fetch(tab, url, opts)`** — page-context fetch (runs inside the tab's origin, so cookies + same-origin headers are automatic). Replaces the `eval-file` temp-file + double-JSON-unwrap dance.\n- **`browser.websocket.on(tab, …).filter({…}).forward({ sink })`** — declarative WebSocket observer with a closed sink set (`webhook` / `scoop` / `vfs` / `log`). **Required** for any new WS-watch use case; do not author page-context `WebSocket.prototype` patches in skill code.\n- **`require('sliccy:http').client({ baseUrl, token, headers, retry })`** — `get`/`post`/`put`/`delete` with merged headers, lazy token resolution, and Retry-After-aware backoff for `retry.on` statuses.\n- **`require('sliccy:skill')`** — `dir` / `root` / `refs` / `assets` / `config()` / `token(providerId)`: replace the per-skill `process.argv[1]` dirname math, ad-hoc `.config` JSON readers, and bespoke `oauth-token` shell-outs. `refs`/`assets` resolve from the skill root (parent of the `scripts/` path segment, including nested helpers).\n\nShip a `.jsh` when the skill needs deterministic, parameterizable behavior the agent shouldn't have to re-derive each time (e.g. a `slicc-handoff` helper, a custom diff formatter, a domain-specific lint).\n\n### `.bsh` — browser shell scripts\n\n`.bsh` files auto-execute when the browser navigates to a matching URL:\n\n- **Filename = hostname pattern**: `-.okta.com.bsh` matches `*.okta.com`.\n- **`// @match` directive**: restrict to specific URL patterns in the first 10 lines.\n- Same execution engine as `.jsh`.\n\nUse `.bsh` for site-specific automations — auto-fillers, lick-emitters, or page transforms that should run whenever the user lands on a particular host.\n\n## Filesystem at a glance\n\nThe VFS is stored in IndexedDB; it survives tab closes and refreshes. The `mount` shell command bridges remote storage (local folders, S3-compatible, Adobe DA) into VFS paths — see `/workspace/skills/mount/SKILL.md`.\n\nThe VFS supports symbolic links transparently:\n\n```bash\nln -s /workspace/skills /workspace/skill-link # Create symlink\nreadlink /workspace/skill-link # Read link target\nls -la /workspace/ # Shows symlinks with -> target\n```\n\n`cat`, `read_file`, `write_file` etc. follow symlinks automatically.\n\n**Mount points must be empty.** Mounting over existing files is blocked so built-in skills and scripts stay discoverable. `ln -s` mounted files into the place where you need them.\n\n## Don't\n\n- Don't ship a skill without a description that starts with \"Use this when...\" — the trigger field IS the skill from the agent's perspective.\n- Don't put `name:` in Title Case. Lowercase kebab-case. Match the folder.\n- Don't dump shell-command catalogs into a SKILL.md just because they're related — `commands` already lists them. Skills are for **patterns and policy**, not reference material.\n- Don't author skills under `.agents/skills/` or `.claude/skills/`. Those roots are for compatibility discovery from other agents.\n";export{e as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e='---\nname: mount\ndescription: |\n Use this whenever the user asks to mount anything — local folders, S3\n buckets, S3-compatible services (Cloudflare R2, MinIO), or Adobe da.live\n / AEM Document Authoring repos. Read this skill BEFORE deciding which\n backend to use; do NOT default to a local file picker when the user\n names a remote service. Covers credential setup with profile-namespaced\n `secret set` keys (e.g. `s3.aws.access_key_id`) or the extension Options\n page, the right `mount --source` invocation per intent, and common errors\n (EACCES on missing credentials, EBUSY on concurrent edits, EFBIG on\n oversized files).\nallowed-tools: bash, read_file, write_file, edit_file\n---\n\n# Mount\n\nThe `mount` shell command bridges remote storage into the VFS. After mounting, `read_file`, `write_file`, `edit_file`, and `bash` (with `cat`, `ls`, etc.) all work against the remote source as if it were a local directory. Four backends:\n\n| Backend | Source URI | Auth |\n| ------- | ----------------------------- | ------------------------------------------------------ |\n| Local | (no `--source`) | OS file picker — cone-only, fails in scoops |\n| S3 | `s3://<bucket>[/<prefix>]` | Profile-namespaced secrets (`s3.<profile>.*`) |\n| DA | `da://<org>/<repo>[/<path>]` | Adobe IMS bearer (reuses the Adobe LLM provider login) |\n| AEM | `aem://<org>/<site>[/<path>]` | Adobe IMS bearer (same login as DA) |\n\n## Choosing a backend from user intent\n\nWhen the user asks to "mount X", read the request literally before defaulting to local:\n\n| User says | Use this backend |\n| -------------------------------------------------- | ------------------------------------------------------- |\n| "mount my Documents folder" / "mount /tmp" | Local — `mount /mnt/documents` |\n| "mount this S3 bucket: s3://my-bucket/foo" | S3 — `mount --source s3://my-bucket/foo /mnt/s3` |\n| "mount this R2 bucket" | S3 with a custom-endpoint profile (R2 is S3-compatible) |\n| "mount the AEM DA repo for org/site" | DA — `mount --source da://<org>/<site> /mnt/da` |\n| "mount this Adobe DA project" / "mount da.live" | DA |\n| "mount this Helix 6 site" / "mount the Source Bus" | AEM — `mount --source aem://<org>/<site> /mnt/aem` |\n| "mount this S3-compatible storage" (MinIO, etc.) | S3 with a custom-endpoint profile |\n\nIf the URL scheme is `s3://`, `da://`, or `aem://`, the choice is unambiguous — don\'t ask. If the user gives a hostname or describes a service without a URL, ask one specific clarifying question (e.g. "Is this the AEM Document Authoring service at da.live, or a different system?") rather than offering a menu of generic options.\n\n**Don\'t default to local when the user mentions a remote service name.**\n\n### DA or AEM? You don\'t have to know — but read the mount output\n\nA site upgraded to the Helix 6 architecture keeps its `da.live` authoring UI and its `<org>/<site>` name, but its content moves to the Source Bus on `api.aem.live`. `da://` handles this for you: it probes the site config before mounting and re-routes to the Source Bus when that is where the content lives, printing a note on stderr. So `mount --source da://<org>/<site>` is always safe to reach for.\n\nTwo things follow:\n\n- **Read the stderr note.** `mount: <org>/<site> is on Helix 6 …` means the mount landed on `aem://` — that is correct, not a warning to work around.\n- **A `da://` mount can now fail with `could not determine the content source`.** That means the site config was unreadable (usually: no Adobe login), not that the site is missing. Fix the login, or pass `--backend da` / `--backend aem` if you already know which store holds the content. Don\'t retry the same command hoping for a different answer.\n\n## Setting up credentials before the first mount\n\n### S3 / R2 / MinIO\n\nS3 mounts read credentials from profile-namespaced secrets. Set them via the `secret` command before mounting. The agent never sees real secret values; only the server-side sign-and-forward handler does.\n\n```bash\n# AWS S3 (default profile)\nsecret set s3.default.access_key_id AKIA... --domain "*.amazonaws.com"\nsecret set s3.default.secret_access_key ... --domain "*.amazonaws.com"\nsecret set s3.default.region us-east-1 --domain "*.amazonaws.com"\n\n# Cloudflare R2 (uses a custom endpoint, requires path-style addressing for some setups)\nsecret set s3.r2.access_key_id ... --domain "*.r2.cloudflarestorage.com"\nsecret set s3.r2.secret_access_key ... --domain "*.r2.cloudflarestorage.com"\nsecret set s3.r2.endpoint https://<account>.r2.cloudflarestorage.com --domain "*.r2.cloudflarestorage.com"\n# Optional — only set this if R2 returns "Bucket name was not in expected format" at first read:\nsecret set s3.r2.path_style true --domain "*.r2.cloudflarestorage.com"\n```\n\nPer-profile keys: `access_key_id` and `secret_access_key` are required; `region` (default `us-east-1`), `endpoint` (custom host for R2/MinIO), `session_token` (for STS), and `path_style` (`"true"` for path-style addressing) are optional.\n\nIn **CLI / Electron mode** secrets live in `~/.slicc/secrets.env` (or macOS Keychain via swift-server). In **extension mode** they live in `chrome.storage.local` and the `secret` command writes to it directly.\n\n### Adobe da.live and AEM Source Bus\n\nDA and AEM mounts both use the Adobe IMS bearer token from the existing Adobe LLM provider — there are no DA-specific secrets to set. **If the user has not logged into the Adobe LLM provider yet, the first mount will fail with `EACCES`. Tell them to log in via Settings → Providers → Adobe (or run `oauth-token adobe`) first.**\n\n## Mounting\n\n```bash\n# Local (interactive picker, cone only)\nmount /mnt/local\n\n# S3 — bucket + optional prefix\nmount --source s3://my-bucket /mnt/s3\nmount --source s3://my-bucket/site --profile aws /mnt/aws\n\n# Cloudflare R2 — same s3:// scheme, different profile (custom endpoint)\nmount --source s3://my-r2-bucket/path --profile r2 /mnt/r2\n\n# Adobe da.live — org + repo (probes the site config, re-routes if Helix 6)\nmount --source da://my-org/my-repo /mnt/da\n\n# AEM Helix 6 Source Bus — org + site, no probe needed\nmount --source aem://my-org/my-site /mnt/aem\n```\n\nUseful flags:\n\n- `--profile <name>` — selects which `s3.<profile>.*` keys to use (S3 only). Defaults to `default`.\n- `--backend <da|aem>` — force the Adobe backend instead of probing the site config. Reach for it only when the probe fails or you know it is wrong.\n- `--no-probe` — skip the mount-time `HEAD bucket` / `GET /list` probe. Use when you want the mount to land even if the source is temporarily unreachable; first read/write will surface any auth errors instead. It does **not** skip the `da://` content-source probe.\n- `--max-body-mb <n>` — override the per-mount body-size limit. Defaults: S3 25 MB, DA/AEM 5 MB. Files exceeding this throw `EFBIG` before bytes flow.\n\n## Lifecycle\n\n```bash\nmount list # show all active mounts\nmount --list # same as `mount list` (`-l` also works)\nmount unmount /mnt/r2 # tear down (cache stays for next mount within TTL)\numount /mnt/r2 # alias for `mount unmount` (same flags, same exit codes)\nmount unmount --clear-cache /mnt/r2 # tear down + drop cached listings/bodies\nmount refresh /mnt/r2 # re-walk the source and diff against cache\nmount refresh --bodies /mnt/r2 # also conditionally re-fetch changed bodies\n```\n\n`umount <path>` is a plain alias for `mount unmount <path>` — same parser, same `--clear-cache`, same exit codes; only the error prefix says `umount:`. Use whichever spelling the user typed. Unmounting a path that is not mounted is a no-op (not an error) under both.\n\n`mount refresh` prints a structured summary: `Refreshed /mnt/r2: +2 -1 ~3 (47 unchanged, 0 errors)`. Use it after you know the remote changed externally and you want the local view to catch up before the 30 s TTL expires.\n\n## Index state and bounds (`mount list`)\n\nEach mount is indexed in the background for fast file discovery and listings; `mount list` shows each mount\'s index state. The walk is bounded — defaults (raised 10×): max directory depth **400**, max total entries **2,000,000**. Two env vars override them: `SLICC_MOUNT_INDEX_MAX_DEPTH` and `SLICC_MOUNT_INDEX_MAX_ENTRIES` (an invalid value falls back to the default with a warning).\n\nWhen a bound is hit the index is **skipped** (reads still work via the slow fallback) and `mount list` shows a distinct cause, so advise the right remedy:\n\n- `directory nesting exceeded the depth limit` → raise `SLICC_MOUNT_INDEX_MAX_DEPTH` or unmount.\n- `mounted tree is too large` → raise `SLICC_MOUNT_INDEX_MAX_ENTRIES` or unmount (this is **not** a cycle).\n- `self-referential mount cycle detected` → a real, confirmed cycle; unmount it.\n- `index error: <message>` → any other indexing failure.\n\nDon\'t call a large or deep mount "cyclic" — only `self-referential mount cycle detected` means a true self-reference.\n\n## Reading and writing once mounted\n\nTreat the mount path like any other VFS directory:\n\n```bash\nls /mnt/da\nread_file /mnt/da/index.html\nwrite_file /mnt/da/new-page.html "<html>..."\nedit_file /mnt/da/index.html # via the standard edit_file tool\nrm /mnt/da/old.html\n```\n\nReads and writes use TTL + ETag caching (30 s default). Reads are zero-RTT within TTL. Writes use `If-Match` / `If-None-Match: *` for conflict detection.\n\nOn an AEM (Source Bus) mount there are no ETags — the API exposes only `last-modified` — so conflict detection uses modification time and only covers files you read first. A blind `write_file` to a path you never read will overwrite whatever is there. Read before you write on `aem://` mounts.\n\n## Common error patterns\n\n| Error | What it means |\n| ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `mount: probe failed for s3://… — profile \'aws\' missing required field \'access_key_id\'.` | The user hasn\'t set credentials yet. Walk them through `secret set s3.<profile>.*`. |\n| `EACCES: s3 access denied` | Wrong credentials, wrong region, or the bucket policy denies the user. |\n| `EACCES: da access denied` / `EACCES: aem access denied` | IMS token expired or user not authed against the Adobe provider. |\n| `mount: could not determine the content source for da://…` | The site config probe failed — usually no Adobe login. Fix the login, or pass `--backend da` / `--backend aem`. Don\'t just retry. |\n| `EBUSY: remote modified since last read — re-read and retry` | Concurrent writer changed the file. Re-read with `read_file` and retry the edit. |\n| `EFBIG: body exceeds maxBodyBytes` | File is over the per-mount size limit (S3 25 MB, DA/AEM 5 MB). Use shell tools (`aws s3 cp`) for very large files instead, or pass `--max-body-mb <n>` at mount time. |\n| `mount: cannot mount local directories from a scoop (no UI).` | Local mounts need a user gesture. Either ask the cone to mount, or use S3/DA which work in scoops. |\n\n## When asked to "explore" a mounted DA or S3 source\n\nAfter mounting, prefer `bash: ls` over `read_file` for navigation — it\'s instant within the TTL window because the listing is cached. Only `read_file` files you actually intend to read; every read is a network round-trip on the first call.\n\nFor DA specifically: the `/list` endpoint doesn\'t include file sizes, so `ls -l` triggers one HEAD per file the first time, then caches. Subsequent `ls -l` within 30 s is free.\n\nFor AEM: listings _do_ carry size and mtime, so `ls -l` costs one listing and no per-file round-trips. The size shown is the stored (compressed) size until the file has been read, so it can be well under what `read_file` returns. Empty folders don\'t exist on the Source Bus — a directory disappears when its last file is deleted.\n\n## Don\'t\n\n- Don\'t suggest the user install a separate AWS CLI / da.live SDK — `mount` is the integration point.\n- Don\'t try to `cd` into a remote mount before mounting; `bash`\'s working directory is independent of mount setup.\n- Don\'t ask "do you have credentials" if the user has already named a service — try the mount first, surface the actionable error from the probe, and walk them through the specific `secret set` commands.\n- Don\'t work around a `could not determine the content source` failure by switching to `--no-probe` — that flag doesn\'t skip the content-source probe, and the failure is telling you the login is missing.\n- Don\'t fall back to a local mount if the user mentioned a remote service. Default to clarifying which remote backend, not which directory to pick.\n';export{e as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e='---\nname: upgrade\ndescription: |\n Use this when you receive an `[Upgrade Event: x.y.z→a.b.c]` lick — fired on\n boot whenever the bundled SLICC version differs from the previous run. The\n lick renders a binary action card: `lick_confirm` to Update workspace files\n (three-way merge of bundled `vfs-root` files against the user\'s local edits)\n or `lick_dismiss` to clear it. Reviewing the changelog from GitHub, and\n checking installed skills for updates with `upskill list --outdated`, are\n separate steps you can run first. Never auto-applies; the user resolves the\n card.\nallowed-tools: bash, read_file, write_file, edit_file\n---\n\n# Upgrade\n\nWhen SLICC boots and discovers that the bundled version (baked into the build at release time from the root `package.json`) differs from the version it was last seen running, it emits an `upgrade` lick to the cone. This skill describes how to react.\n\n## Event shape\n\nYou receive a message like:\n\n```text\n[Upgrade Event: 0.4.1→0.5.0]\n\nSLICC was upgraded from `0.4.1` to `0.5.0`.\nReleased: 2026-04-15T12:00:00Z\n\nUse the **upgrade** skill (...)\n```\n\nThe two version strings (`from`, `to`) are valid git tags on `https://github.com/ai-ecoverse/slicc` — the public source repository. The lick message carries a `Lick ID:` line — you resolve the card with that id.\n\n## What to do when you receive an upgrade lick\n\nThe runtime renders the upgrade lick as a binary action card automatically — you do not render a sprinkle. The card has exactly two outcomes, which you drive with the lick tools using the `Lick ID` from the message:\n\n- **`lick_confirm` → Update workspace files.** This runs `upgrade apply` with the stored release versions. Only confirm once the user has decided to pull the new files.\n- **`lick_dismiss` → clear the card.** Use this when the user wants to skip the merge; nothing is changed and the card mutes (✗). The lick will not fire again until the next upgrade.\n\nThe card flips to ✓ on confirm / muted ✗ on dismiss. Never auto-run the merge — the user must choose. Reviewing the changelog is **not** a card action; it is a separate step you can run first to help the user decide.\n\n**Before `lick_confirm` or `lick_dismiss`, check `list_scoops`.** A scoop that is `processing` may lose its in-flight work when the runtime moves to the new version. There is **no notification** — the scoop can look `ready` / finished, indistinguishable from a clean completion. Wait until every scoop is idle, or expect to re-feed any that were still working. Idle scoops survive.\n\n## Which version am I running?\n\n`uname -r` prints the running version. `upgrade status` adds the last-booted one, whether a merge is pending, and the exact `upgrade apply` line to run when it is — that is where `--from`/`--to` come from without a card on screen. Realm scripts read `globalThis.SLICC_VERSION`.\n\n## Changelog review (separate step — not a card action)\n\nBefore the user decides, you can fetch the GitHub compare API for the two tags and summarize the result. This is optional and independent of the card; it does not resolve the lick.\n\n```bash\n# The repo is public — no auth required for the compare endpoint.\n# stdin here is fully buffered, so read() drains the whole response in one shot.\ncurl -sSL "https://api.github.com/repos/ai-ecoverse/slicc/compare/v${FROM_VERSION}...v${TO_VERSION}" \\\n | node -e \'const j=JSON.parse(process.stdin.read()||"{}");console.log((j.commits||[]).map(c=>"- "+c.commit.message.split("\\n")[0]).join("\\n"))\'\n```\n\nShow the conventional-commit messages grouped by type (`feat`, `fix`, `chore`, ...). If the compare returns 404 (tags missing), fall back to the GitHub releases page URL: `https://github.com/ai-ecoverse/slicc/releases/tag/v${TO_VERSION}`.\n\n## Applying workspace files (the `lick_confirm` action)\n\n`lick_confirm` runs the browser-native command below through the cone shell using the versions stored with the lick. Do not run a second merge after confirming.\n\n```bash\nupgrade apply --from="${FROM_VERSION}" --to="${TO_VERSION}"\n```\n\nThe command discovers bundled files at both release refs under `/workspace/skills`, `/shared/sprinkles`, `/shared/sounds`, and `/etc`, plus the single file `/shared/MEMORY.md`; prefetches and preflights every path; then applies safe updates with `VirtualFS` and the built-in three-way merge. `/shared/MEMORY.md` (the memory-curator contract) and the `/etc` policy files (`sudoers`, `models`, `llmstxtignore`) are seeded only when absent, so this merge is the only way a rule change in them reaches an existing profile — including approval rules such as the `Write /etc/models` gate. Applying a change to `/etc/sudoers` still raises its own approval prompt: the card authorizes the merge, not the policy edit. Its JSON output classifies every bundled path as `auto-applied`, `merged-clean`, `kept-local`, `needs-review`, `unchanged`, or `added-new`.\n\nAn exit code of `1` means discovery/fetch failed or at least one path needs review. Conflicts are written to the reported collision-safe sidecar while the live file remains unchanged. Show the JSON summary and sidecar paths to the user; never copy conflict markers into the live file automatically. The command never deletes local-only files.\n\nThe command can also be run directly with explicit release versions for manual recovery, but an upgrade card still requires the user\'s confirmation before changing files.\n\n## Also check installed skills (separate step — not a card action)\n\nThe card only covers **bundled** files (`/workspace/skills`, `/shared/sprinkles`, `/shared/sounds`, `/shared/MEMORY.md`, `/etc`). Skills the user installed themselves with `upskill` are never touched by it, and they drift silently — a stale one can sit months behind upstream while still loading fine.\n\nA new SLICC release is a good moment to check them. This is read-only:\n\n```bash\nupskill list --outdated\n```\n\nIt reuses the same classification as `upskill update --dry-run`: each skill\'s `.upskill` provenance record (source repo, ref, resolved commit, file list) compared against the latest commit that touched the recorded upstream path. Only skills a bare `upskill update` would actually change are listed. Skills with no record are omitted with a skipped count — they are not printed as current. Exit 0 whether or not anything is stale; a skill whose check itself failed still exits 1. Use `upskill update --dry-run` when you want the per-file `unchanged` / `updated` / `added` / `removed` / `kept-local` breakdown.\n\nReport what would change and let the user decide. To apply:\n\n```bash\nupskill update # every skill with provenance\nupskill update <skill> # just one\n```\n\nNotes worth knowing:\n\n- **Dotfiles are never touched.** `upskill` will not modify or delete a dotfile in a skill directory, so credentials (`scripts/.config`) and the `.upskill` record survive updates and `--force` reinstalls. Never hand-copy a credential file "to be safe" — it is already safe, and moving it can break the skill.\n- **`kept-local` is not a failure.** It marks dotfiles and files the user added themselves; leaving them is the correct outcome.\n- A skill installed before provenance tracking reports `no install provenance`. If the user knows where it came from, record it in place — `upskill update <skill> --from <owner>/<repo> --dry-run` first, then without `--dry-run`. That first update never deletes anything, because nothing is attributable to a previous install yet.\n- **The sweep tells you what it did not check.** Skills with no `.upskill` record are listed under `Skipped <n> skills with no install provenance` and the closing line is scoped to the ones it checked (`All 13 skills with provenance are current.`). That is not a failure and the exit code stays 0: the runtime-bundled skills legitimately have no record and `upgrade apply` keeps those current. Pass the rest on to the user — one `--from` per skill is all it takes for the sweep to cover them from then on. Do not read an unqualified `All skills are current.` into a run that printed a skipped list.\n\n## Do not\n\n- Do not call `lick_confirm` or `lick_dismiss` while `list_scoops` shows a scoop `processing`. In-flight work can drop to `ready` with no notification; wait until scoops are idle, or expect to re-feed them.\n- Do not run `upgrade apply` before the user confirms. Confirmation runs it automatically; dismissal runs nothing.\n- Do not delete files that no longer exist in the new release — many users name-collide their own scripts with bundled ones; deletion is too dangerous to automate.\n- Do not modify files outside `/workspace/skills/`, `/shared/sprinkles/`, `/shared/sounds/`, `/shared/MEMORY.md`, and `/etc/` without the user explicitly extending the scope.\n- Do not run `upskill update` (without `--dry-run`) unless the user asks for it — `upskill list --outdated` (or `upskill update --dry-run`) is the safe default when you are volunteering the check.\n- Do not advance the bundled version marker yourself. The runtime advances it automatically once this lick has been routed; if the user dismisses, the lick will not fire again until the next upgrade.\n';export{e as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e='---\nname: sprinkles\ndescription: |\n Use this when the user wants a persistent UI panel — a dashboard, form,\n editor, report, or visualization that lives alongside the chat. Sprinkles are\n `.shtml` files under `/shared/sprinkles/` rendered in the side rail or as a\n full-screen tab. For ephemeral inline widgets, use dips instead. Covers\n creation, modification, layout constraints, the cone-to-scoop orchestration\n rules, the `slicc.*` bridge API, and `sprinkle chat` for blocking inline\n prompts.\nallowed-tools: bash, read_file, write_file, edit_file\n---\n\n# Sprinkles\n\n`.shtml` files in `/shared/sprinkles/` become interactive UI panels. Use them for dashboards, forms, and visualizations that persist alongside the chat.\n\n## Two rendering modes\n\n- **Fragment mode** (default): plain HTML fragments injected into the sidebar. Do NOT use `<!DOCTYPE html>`, `<html>`, `<head>`, `<body>`, or custom CSS — use the built-in `.sprinkle-*` classes. Scripts get a `slicc` bridge object automatically.\n- **Full-document mode**: complete HTML documents (starting with `<!DOCTYPE html>` or `<html>`) render inside sandboxed iframes. Use this for complex layouts with custom CSS, sidebars, split panes, modals, or canvas/SVG visualizations. The bridge script is auto-injected — `window.slicc` and `window.bridge` are available. The parent page\'s S2 theme tokens are injected automatically.\n\nPick full-document mode when you need custom CSS beyond `.sprinkle-*` classes, complex layouts (sidebar + main, split panes, tabs), or interactive canvas/SVG.\n\n## Light & dark mode\n\nTheming is **automatic**. The parent injects its S2 tokens into every sprinkle and toggles a `.theme-light` class on the sprinkle root — the iframe\'s `<html>` in full-document mode, or the outermost container injected into the sidebar in fragment mode — whenever the user flips the parent theme. All `var(--s2-*)` tokens swap in lockstep. **Never hard-code colors for one theme.**\n\n- Use S2 tokens (`var(--s2-content-default)`, `var(--s2-bg-layer-2)`, etc.) for everything — see `style-guide.md` for the full reference.\n- For one-off colors not covered by an S2 token, use CSS `light-dark(<light>, <dark>)` and set `color-scheme: light dark` on an ancestor (the root in full-document mode, the outermost container in fragment mode). `light-dark()` returns the dark value only when `color-scheme` is set. This intentionally follows the user\'s OS `color-scheme` preference for one-off custom colors; S2 tokens still track the parent app\'s class-based theme toggle in lockstep.\n- **Do NOT use `@media (prefers-color-scheme: ...)`** to swap colors that should mirror the parent app\'s theme. The parent\'s theme toggle is a class on the sprinkle root, not the OS preference, so media queries desync from the actual app theme. Use S2 tokens (class-driven) or `light-dark()` (OS-driven, for one-off custom colors only) instead.\n\n## Layout & viewport\n\nSprinkles open in **one of four viewport contexts**, and you must design for the narrowest:\n\n| Float | Default viewport | Multi-column safe? |\n| -------------------------------- | -------------------------------------- | ----------------------- |\n| Desktop (CLI / Electron) sidebar | Narrow rail (≈ 360 px) | No — single column only |\n| Desktop full-screen pop-out | Full window | Yes |\n| iOS / Sliccstart app frame | Full-width but always single-column UX | No |\n| Chrome extension side panel | Narrow (≈ 360 px, fixed by Chrome) | No |\n\n**Default to a single-column layout.** Multi-column layouts (sidebar + main, split panes, three-up grids) only render usefully when the user explicitly pops the sprinkle to full-screen on desktop. They look broken in the rail and on iOS.\n\nIf you genuinely need multi-column UI, do all of the following:\n\n1. Build it in **full-document mode** so you can use grid / flex / media queries cleanly.\n2. Use `@media (max-width: 600px)` (or similar) to collapse to single column at narrow widths so the sidebar/iOS view still works.\n3. Tell the user in your reply that this sprinkle is "best viewed full-screen — pop it out from the rail header."\n\nFor dashboards with many widgets, prefer a vertical stack of `.sprinkle-card` blocks over a grid. The card stack is responsive by default and looks good in both rail and full-screen.\n\n## Arranging panels with `layout`\n\nThe `layout` shell command arranges panels, independent of what\'s inside each one.\nEvery panel is the same kind of thing: chat, each tool panel, and each sprinkle.\n\n```bash\nlayout list # shipped presets (there is one: the default)\nlayout set <preset> # reset to a shipped arrangement\nlayout save <name> # save the CURRENT arrangement as JSON\nlayout load <name> # load a saved layout or the shipped default\nlayout open <surfaceId> <zone> # place a panel (zone: top|left|middle|right|bottom)\nlayout move <surfaceId> <zone> # relocate a placed panel\nlayout close <surfaceId> # remove it (chat is pinned; it can move, never close)\nlayout size <surfaceId> [--width <px|%>] [--height <px|%>] # exact, pixel-perfect\nlayout chat <zone> # move the chat panel\nlayout reset # back to the default\n```\n\nSLICC ships exactly ONE arrangement (the default). To give a skill its own layout,\narrange the panels and `layout save <name>` — do not expect named presets beyond the\ndefault to exist.\n\nOpening a sprinkle auto-places it, so `layout open` is only for overriding that.\n`layout size` is the programmatic counterpart to a user dragging a divider — use it\nwhen you want an exact size rather than a proportion.\n\nThe five zones are `top`, `left`, `center`, `right`, `bottom` — Java `BorderLayout`.\nThey sit inside the fixed chrome, so `top` is below the scoop/budget strip and\n`left`/`right` are inboard of the rails. A zone holds several panels at once, so\nplacing two sprinkles in `left` puts them both there (stacked by default).\n\n### Writing a layout JSON by hand\n\nA layout document has two sections, and **you only ever write `zones`**:\n\n```jsonc\n{\n "version": 1,\n "id": "my-layout",\n "base": {\n // DO NOT AUTHOR. The fixed chrome — scoop/budget strip, both rails. Copy it\n // verbatim from an existing document, or omit it to keep the shipped chrome.\n "docks": [...],\n // YOURS. Which panels go in which of the five zones.\n "zones": {\n "top": ["sprinkle:status-panel"],\n "left": ["chat"],\n "right": ["sprinkle:main-window"],\n "sizes": { "top": "180px", "left": "40%" }\n }\n }\n}\n```\n\nA "status bar across the top, below the SLICC bar, between the rails" is\n`zones.top` — NOT a second `docks` entry with `edge: "top"`. `docks` is the app\'s\nown chrome; adding to it puts your panel in the same strip as the scoop switcher and\nthe price counter. Reach for `zones` every time.\n\nPrefer `layout save` over hand-writing JSON: arrange the panels with `layout\nopen`/`move`/`size`, then save. Hand-editing is for shipping a layout with a skill.\n\nThe user can also rearrange by hand — there is no edit mode to enter. Hovering a\npanel reveals a grip in its top-left corner; grabbing it shows the five zones as a\ncompass. Two kinds of seam drag to resize: between panels inside a zone, and\nbetween the zones themselves. Their arrangement\npersists across reloads, so do NOT re-issue `layout` commands to "restore" a layout\nyou set earlier — you would be overwriting what they did.\n\nOn a narrow viewport (extension side panel, iOS, ≤700px) multi-panel arrangements\ncollapse to chat alone, so still design each sprinkle single-column-safe per the\ntable above.\n\n## Are panels always sprinkles?\n\nNo — but a sprinkle is the only kind YOU can create, and that is the intended path.\n\n| Kind | Who makes it | How |\n| --------------- | ---------------- | -------------------------------------------------------------- |\n| Sprinkle panel | you | write `.shtml`, `sprinkle open <name>` — this section |\n| Built-in panel | ships with SLICC | chat, both rails, the top strip, files/terminal/memory/monitor |\n| Layout document | you | JSON naming which panels go where; `layout save`/`load` |\n\nSo: to add UI, write a sprinkle. To rearrange existing UI, write a layout document\nor issue `layout` commands. There is no third mechanism, and no need for one — a\nsprinkle is a full HTML document with a bridge, which covers what a custom panel\nwould.\n\nA sprinkle IS tied to a scoop, and that is deliberate: whoever creates one owns it\nfor its lifetime and handles its lick events (see "Handling lick events" below).\nA layout document is not tied to anything — it only names panel ids.\n\n### Shipping UI with a skill\n\nA layout document saved under `/workspace/layouts/<name>.json` can be loaded with\n`layout load <name>`, so a skill can ship both its sprinkles and the arrangement\nthat presents them. Write the sprinkles, open them, arrange, then `layout save\n<name>` to capture what is on screen.\n\n## Creating a sprinkle\n\n1. `read_file /workspace/skills/sprinkles/style-guide.md` — **always read first** before writing any sprinkle.\n2. **Pick a rail icon** that matches the sprinkle\'s purpose (see "Sprinkle icon" below). Every new sprinkle MUST declare an icon — the generic Sparkles default is reserved for sprinkles that genuinely have no thematic anchor.\n3. `write_file` to `/shared/sprinkles/<name>/<name>.shtml` (follow the style guide templates).\n4. `bash` → `sprinkle open <name>`.\n5. **CRITICAL: do NOT finish or send a completion message.** You own this sprinkle for its entire lifetime. The cone will send you follow-up instructions (modifications, lick events) via `feed_scoop`. If you finish, you lose your context and cannot handle future work on this sprinkle.\n\n### Where sprinkles are discovered\n\n`sprinkle list` and the rail only see `.shtml` files under `/shared`,\n`/workspace`, `/scoops` and `/home`, no more than six directory levels deep, and\nnever inside `node_modules`, `dist`, `build`, `coverage`, or any dot-directory.\nA `.shtml` written to `/tmp`, `/mnt`, `/etc` or a deeply nested build folder\nwill not be listed — write to `/shared/sprinkles/<name>/<name>.shtml`. (`open\n/path/to/file.shtml` still opens any path directly.)\n\n### Updating a sprinkle (when you receive follow-up instructions)\n\n1. Edit `/shared/sprinkles/<name>/<name>.shtml` with the requested changes.\n2. Reload: `sprinkle reload <name>` — keeps the panel in place and sized. Do not `close`+`open` unless the sprinkle is actually closed; `open` on an already-open sprinkle is a no-op.\n3. Do NOT finish — stay ready for more instructions.\n\n### Handling lick events (when the cone forwards a user interaction)\n\nThe cone will send you a message with the lick action and your sprinkle name. Only modify YOUR sprinkle — the one matching your scoop name. Process the action and push updates:\n\n- `bash` → `sprinkle send <name> \'{"key":"value"}\'` to push data to the sprinkle\'s `slicc.on(\'update\', ...)` handler.\n- Or edit the `.shtml` file and reload if the UI structure needs to change.\n- Do NOT finish — stay ready for more events.\n\n## Sprinkle icon\n\nEach sprinkle gets its own glyph in the rail so users can tell them apart at a glance. Declare it in the `.shtml`. Three formats, in order of preference:\n\n**1. Lucide icon name** (preferred — covers ~1500 icons from [lucide.dev/icons](https://lucide.dev/icons)):\n\n```html\n<link rel="icon" href="music" />\n```\n\nUse the kebab-case name from lucide.dev. Common picks: `music`, `code`, `terminal`, `chart-bar`, `chart-line`, `calendar`, `calendar-clock`, `clock`, `image`, `file-text`, `globe`, `book-open`, `compass`, `gauge`, `wrench`, `palette`, `bug`, `flask-conical`, `database`, `cloud`, `package`, `shopping-cart`, `dollar-sign`, `mail`, `message-square`, `bell`, `users`, `user`, `settings`, `sparkles`.\n\n**2. SVG file in the sprinkle\'s directory** (when no Lucide icon fits):\n\n```html\n<link rel="icon" href="/shared/sprinkles/<name>/icon.svg" />\n```\n\nAuthor the SVG with `viewBox="0 0 24 24"`. Keep paths simple — the rail renders at 16×16. **Note**: only Lucide icons inherit `currentColor` from the rail; author-supplied SVGs render through `<img>` (script-disabled), so set explicit colors in the SVG itself.\n\n**3. Inline SVG or data URL** (one-off icons, no extra file):\n\n```html\n<link\n rel="icon"\n href=\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="..."/></svg>\'\n/>\n```\n\n**Where to put it**: inside `<head>` for full-document mode, or as the very first element in fragment mode.\n\n**Quoting tip**: when the `href` value contains quotes (inline-SVG data URLs do), wrap the attribute in single quotes: `href=\'data:image/svg+xml;utf8,<svg xmlns="...">...\'`.\n\n**Fallback**: if the icon spec is missing or unresolvable, the rail uses a generic Sparkles glyph.\n\n## Cone orchestration rules\n\nThese are the rules the cone follows when sprinkles are involved. They are absolute.\n\n### Rule 1: One scoop per sprinkle, named identically\n\nScoop name MUST match sprinkle name. Sprinkle `giro-winners` = scoop `giro-winners`.\n\n### Rule 2: Cone never touches sprinkle files or commands\n\nThe cone MUST NOT: write/edit `.shtml` files, run `sprinkle open / close / send`, or handle lick events directly. ALL sprinkle work goes through scoops via `feed_scoop`. **Never handle a lick in the cone.**\n\n### Rule 3: Creating sprinkles\n\nCreate a scoop, then feed it a complete, self-contained brief:\n\n```\nscoop_scoop("giro-winners")\nfeed_scoop("giro-winners", "You own the sprinkle \'giro-winners\'. Your job:\n1. Run: read_file /workspace/skills/sprinkles/style-guide.md\n2. Research the last 3 Giro d\'Italia winners\n3. Pick a rail icon — for cycling, use <link rel=\\"icon\\" href=\\"bike\\" /> in <head>. See the sprinkles SKILL.md \\"Sprinkle icon\\" section for the full list.\n4. Write the sprinkle to /shared/sprinkles/giro-winners/giro-winners.shtml\n5. Run: sprinkle open giro-winners\n6. IMPORTANT: After opening the sprinkle, do NOT finish. Stay ready — you will receive follow-up instructions and lick events for this sprinkle via feed_scoop. Do not send a completion message.")\n```\n\n### Rule 4: Modifying sprinkles\n\nFeed the EXISTING scoop that owns it. Do NOT create a new scoop:\n\n```\nfeed_scoop("giro-winners", "Modify YOUR sprinkle \'giro-winners\' at /shared/sprinkles/giro-winners/giro-winners.shtml:\nAdd an \'Add Previous Year\' button with onclick=\\"slicc.lick({action: \'add-year\'})\\"\nThen reload: sprinkle reload giro-winners\nStay ready for more work.")\n```\n\n### Rule 5: Lick events\n\nForward to the owning scoop, never handle yourself:\n\n```\nfeed_scoop("giro-winners", "Lick event on YOUR sprinkle \'giro-winners\' (/shared/sprinkles/giro-winners/giro-winners.shtml):\nAction: \'add-year\'\nLook up the next previous year\'s Giro d\'Italia winner and update the sprinkle.\nUse: sprinkle send giro-winners \'<json>\' to push data, or edit the .shtml and reload.\nStay ready for more lick events.")\n```\n\n## Cheap interactions via `agent`\n\nWhen a sprinkle button needs to do real work but the owning scoop should NOT be pulled into a turn (it\'s busy, or the work is purely transactional), route the lick handler through `agent` instead.\n\nPattern:\n\n1. User clicks → `slicc.lick({action: \'lookup\', data: {q: \'foo\'}})`.\n2. Cone sees the lick, forwards to the owning scoop with `feed_scoop`.\n3. The scoop\'s reply runs `agent` against a tight allow-list and writes the result back via `sprinkle send`.\n\n```bash\n# Inside the owning scoop\'s reply to a lick:\nresult=$(agent "$TMPDIR" "curl,jq" "Look up \'$Q\' in <api>, return the price.")\nsprinkle send giro-winners "$(jq -n --arg r "$result" \'{result:$r}\')"\n```\n\nWhy this matters: a busy scoop that owns a sprinkle can shell to `agent` to handle a click without growing its own conversation. `agent` is **handoff-free** — the ephemeral sub-scoop doesn\'t notify the cone or the owning scoop on completion. See `/workspace/skills/delegation/SKILL.md` for the full `agent` reference.\n\nThis is the difference between "every click adds a turn to your owning scoop" (expensive, drifts) and "every click is a clean transaction" (predictable, cheap).\n\n## Managing sprinkles via bash\n\n- `sprinkle list` — available sprinkles, plus one `instance:` line per runtime rendering each (leader + reporting followers). `--runtime <id>` narrows it.\n- `sprinkle open <name>` — show a sprinkle in the sidebar.\n- `sprinkle close <name>` — remove it.\n- `sprinkle send <name> \'<json>\'` — push data (single-quote the JSON!). **Broadcasts** to every open instance, leader and followers. Prints the instances reached; exits non-zero when it reached none, so a vanished push is visible.\n- `sprinkle send <name> \'<json>\' --runtime <id>` — push to ONE runtime (ids from `host`; `leader` is the local one). Use it to answer a `request-load` from a specific follower panel.\n- Unknown flags are rejected, so a probe that exits 0 means the flag was honoured.\n- `sprinkle chat \'<html>\'` — show inline HTML in the chat (for quick confirmations / choices). Blocks until the user clicks; returns the lick result as JSON. Use when a tool needs user input mid-execution.\n- `open /path/to/file.shtml` — also opens as a sprinkle.\n\n```bash\nsprinkle chat \'<div class="sprinkle-action-card">\n <div class="sprinkle-action-card__header">Deploy to production?</div>\n <div class="sprinkle-action-card__actions">\n <button class="sprinkle-btn sprinkle-btn--secondary" onclick="slicc.lick({action:\\"cancel\\"})">Cancel</button>\n <button class="sprinkle-btn sprinkle-btn--primary" onclick="slicc.lick({action:\\"deploy\\",data:{env:\\"prod\\"}})">Deploy</button>\n </div>\n</div>\'\n```\n\n## Bridge API\n\nAvailable as `slicc` in `<script>` tags and `onclick` attributes:\n\n- `slicc.lick(event)` — send a lick event to the cone (cone routes to the right scoop). Accepts a string shortcut (`slicc.lick(\'cancel\')` → `{ action: \'cancel\' }`) or `{ action, data?, target? }`. In a leader-rendered panel, `target` is a cone/scoop name or folder alias and overrides the sprinkle\'s configured route for that lick. See payload-shape note below.\n- `slicc.on(\'update\', function(data) {...})` — receive data sent via `sprinkle send`.\n\n> **Payload shape:** the cone reads `event.data` as the payload — top-level extras outside `action`, `data`, and `target` are silently dropped by the sprinkle bridge. Always use `slicc.lick({ action: \'deploy\', data: { env: \'prod\' } })`, not `slicc.lick({ action: \'deploy\', env: \'prod\' })`.\n\n- `slicc.name` — the sprinkle\'s name.\n- `slicc.close()` — close the sprinkle.\n- `slicc.minimize()` — collapse the sprinkle panel (rail icon stays visible; user can click to reopen). Does not close or destroy the sprinkle.\n- `slicc.stopCone()` — stop the cone agent.\n- `slicc.readFile(path)` — read a VFS file (returns `Promise<string>`).\n- `slicc.writeFile(path, content)` — write text content to a VFS file.\n- `slicc.readDir(path)` — list directory entries (returns `Promise<Array<{name, type}>>`).\n- `slicc.exists(path)` — check if path exists (returns `Promise<boolean>`).\n- `slicc.stat(path)` — get file metadata (returns `Promise<{type, size}>`).\n- `slicc.mkdir(path)` — create a directory (recursive).\n- `slicc.rm(path)` — remove a file.\n- `slicc.screenshot(selector?)` — capture sprinkle DOM as base64 PNG data URL. No argument targets `document.body` (fragment mode: the sprinkle container). The target must have non-zero width AND height; zero-sized elements are rejected, not captured. Works in a hidden tab — you do not need to foreground the sprinkle. Note: the screenshot captures a DOM clone using SVG `foreignObject`. External stylesheets, web fonts, and some computed styles may not be fully reproduced; huge serialised SVGs can fail to rasterise. For best results, use inline styles on elements you intend to screenshot.\n- `slicc.captureScreen()` — capture a screen, window, or browser tab via Chrome\'s native picker. Takes no arguments. Returns `Promise<{base64: string, width: number, height: number, mimeType: string}>`. The picker appears immediately; the Promise resolves once the user selects a target and the frame is grabbed. Rejects if the user cancels or screen capture is unavailable. Use `slicc.attachImage(shot.base64, \'screenshot.png\', shot.mimeType)` to send the result to the agent. Key difference from `screenshot()`: this captures external content (any tab/window/screen), not the sprinkle\'s own DOM.\n\n### Shell, agent, and jsh globals\n\nThe bridge also reaches the **same worker shell** that `.jsh` scripts and `node -e` run in, so a sprinkle can call any supplemental command, any `.jsh` script, or spawn a sub-scoop directly from a button. These mirror the capability bridges reached from `.jsh` via `require(\'sliccy:<name>\')` (documented in `/workspace/skills/skill-authoring/jsh-runtime-extensions.md`). Sprinkle code keeps using the trust-gated `slicc.*` namespace; it does not call `require(\'sliccy:…\')` directly.\n\n- `slicc.exec(cmd)` — run a shell command in the worker shell. Returns `Promise<{stdout, stderr, exitCode}>`. A non-zero `exitCode` (or `127` when the shell bridge is unavailable) is returned in the result, never thrown. Use `slicc.exec.spawn(argv)` to bypass shell parsing for untrusted args.\n- `slicc.agent(prompt, opts?)` — spawn a one-shot sub-scoop, feed it `prompt`, block until it completes, and resolve with its final message on `stdout`. Returns `Promise<{stdout, exitCode}>`. `opts`: `{cwd?, allowedCommands?, model?, thinking?, readOnly?}`. Sugar over `slicc.exec` building the `agent` command — the same handoff-free delegation as the `agent` shell command. On failure the error text comes back on `stdout` with a non-zero `exitCode`, never thrown.\n- `slicc.fetch(url, init?)` — proxied, secret-injecting fetch (NOT the iframe\'s CORS-bound native fetch). Resolves to a native `Response` (with `.json()`/`.text()`/`.arrayBuffer()`/`.blob()` and `.ok`/`.status`/`.headers`/`.url`).\n- `slicc.http.client(config)` — higher-level API client over the proxied fetch (`get`/`post`/`put`/`patch`/`delete`). `config`: `{baseUrl?, token?, headers?, retry?, timeoutMs?}`.\n- `slicc.browser.*` — Playwright-style CDP surface (`findTab`, `ensureTab`, `eval`, `evalAsync`, `cookie`, `localStorage`, `fetch`), mirroring `require(\'sliccy:browser\')` in jsh.\n- `slicc.fetchToFile(url, path)` — download a URL (via the proxied fetch) straight to a VFS file; resolves with the byte count.\n- `slicc.readFileBinary(path)` / `slicc.writeFileBinary(path, bytes)` — binary VFS I/O (parity with `require(\'fs\')` in jsh).\n- `slicc.hid.*` / `slicc.serial.*` / `slicc.usb.*` — stateful device surfaces for WebHID / Web Serial / WebUSB (Chromium-only; absent in the cloud / hosted-leader float). Same opaque handles (`hid1`, `serial1`, `usb1`, …) as the `hid` / `serial` / `usb` shell commands and the `require(\'sliccy:hid\' | \'sliccy:serial\' | \'sliccy:usb\')` realm modules — discover via `list()` or trigger the OS picker via `request()` (a button-click is a real user gesture). For HID, `open(handle)` auto-attaches the input-report stream so every `slicc.hid.on(\'inputreport\', cb)` listener receives `{ handle, reportId, data: Uint8Array }` until `close(handle)` or sprinkle teardown. Use this for keyboard configurators, gamepad dashboards, ESP32 monitor panels — anything that needs a persistent device session across multiple button clicks. The realm bridge in `slicc.exec(\'node -e …\')` resets per call, so push handle ops through `slicc.hid|serial|usb` instead.\n\n```typescript\n// HID — stateful across the sprinkle\'s lifetime\nslicc.hid.list(): Promise<HidDeviceInfo[]>\nslicc.hid.request(filters?): Promise<HidDeviceInfo[]> // needs button-click gesture\nslicc.hid.open(handle): Promise<void> // auto-subscribes inputreport\nslicc.hid.close(handle): Promise<void>\nslicc.hid.sendReport(handle, reportId, data: Uint8Array): Promise<void>\nslicc.hid.on(\'inputreport\', cb): void // cb: ({ handle, reportId, data })\nslicc.hid.off(\'inputreport\', cb): void\n\n// Serial — parity list/request/open/close; streaming I/O stays on the realm API.\nslicc.serial.list() / request(filters?) / open(handle, options) / close(handle)\n\n// USB — full transfer surface, so a sprinkle can drive a device itself.\nslicc.usb.list(): Promise<UsbDeviceInfo[]>\nslicc.usb.request(filters?): Promise<UsbDeviceInfo> // needs button-click gesture\nslicc.usb.open(handle) / close(handle, opts?) / reset(handle, opts?): Promise<void>\nslicc.usb.selectConfiguration(handle, value): Promise<void>\nslicc.usb.claimInterface(handle, n, opts?) / releaseInterface(handle, n): Promise<void>\nslicc.usb.on(\'disconnect\' | \'claim-lost\', cb) / off(...): void\n // Handles are shared with the `usb` shell command and `require(\'sliccy:usb\')`.\n // Interface claims are exclusive: a second claim is refused naming the\n // holder; close/reset refuse while another consumer holds a claim unless\n // `{ force: true }`. Forced displacement delivers claim-lost then disconnect.\nslicc.usb.clearHalt(handle, \'in\' | \'out\', endpoint): Promise<void>\nslicc.usb.transferIn(handle, endpoint, length): Promise<{ status, bytes: Uint8Array }>\nslicc.usb.transferOut(handle, endpoint, bytes: Uint8Array): Promise<{ status, bytesWritten }>\nslicc.usb.controlTransferIn(handle, setup, length): Promise<{ status, bytes: Uint8Array }>\nslicc.usb.controlTransferOut(handle, setup, bytes): Promise<{ status, bytesWritten }>\n```\n\nTransfers are on the sprinkle surface, not realm-only, because a `.jsh` cannot\nstream: realm stdout is buffered and delivered when the run completes. Anything\nthat reads a device for as long as it stays interesting — a video stream, a\nsensor feed — has to drive the device from the sprinkle and render as it goes.\n\nYou hand in and get back real `Uint8Array`s; the base64 the iframe boundary\nneeds is handled for you. `UsbDeviceInfo` also carries `configurations`, so an\ninterface can be located by class/subclass/protocol without a descriptor read.\n\n```html\n<button\n id="connect"\n onclick="slicc.hid.request({vendorId:0x320f}).then(d => slicc.hid.open(d[0].handle))"\n>\n Connect keyboard\n</button>\n<script>\n slicc.hid.on(\'inputreport\', ({ reportId, data }) => {\n console.log(\'report\', reportId, [...data].map((b) => b.toString(16)).join(\' \'));\n });\n<\/script>\n```\n\n```html\n<button\n onclick="slicc.exec(\'git status -s\').then(r => slicc.lick({action: \'status\', data: r.stdout}))"\n>\n Refresh status\n</button>\n```\n\n> Prefer `slicc.exec`/`slicc.agent` for transactional work that should NOT grow the owning scoop\'s conversation (see "Cheap interactions via `agent`" above). These run in the same worker shell, so all ~50 supplemental commands and any `.jsh` script are reachable.\n\n**onclick attributes**: always use `slicc` — e.g. `onclick="slicc.lick({action: \'add-year\'})"`. The `slicc` variable is automatically resolved per-sprinkle, so multiple sprinkles won\'t collide. Do NOT use `bridge` or any other variable name in onclick.\n\n**CSS components**: do NOT write custom CSS. Use the built-in `.sprinkle-*` classes: cards, tables, badges, buttons, text fields, progress bars, meters, layout utilities, and more. For inputs use `class="sprinkle-text-field"`, never inline border/padding styles. Run `read_file /workspace/skills/sprinkles/style-guide.md` for the full component reference with markup examples.\n\n## Built-in sprinkles\n\nSLICC no longer ships with a catalog of pre-built sprinkles. The only `.shtml` under `/shared/sprinkles/` is `welcome/`, which backs the inline first-run welcome dip — not a panel sprinkle. **Always create sprinkles from scratch** for what the user is asking for, following the "Creating a sprinkle" flow above. Do not assume a built-in sprinkle name exists.\n';export{e as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e}from"./rolldown-runtime-Dd_uD5pT.js";import{t}from"./logger-LESFN7Hj.js";import{n,r}from"./panel-rpc-BPwAqRch.js";import{n as i,o as a,t as o}from"./providers-C2bj3jKv.js";import"./proxied-fetch-DEK8DT-z.js";import{o as s,t as c}from"./api-endpoint-3qEcn7BY.js";import{u as l}from"./core-C9qgaWgE.js";import{i as u,n as ee,r as d}from"./compat-uEPf6-XW.js";import{t as f}from"./secrets-bridge-client-ZpBL1Zhl.js";import{i as te,r as ne,t as re}from"./bedrock-camp-compat-99DjWBHV.js";import{o as ie}from"./claude-model-version-m5AAX9Oe.js";const ae=`slicc_oauth_extra_domains`;function p(e){try{let t=e.getItem(ae);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 oe(e,t){try{e.setItem(ae,JSON.stringify(t))}catch(e){throw Error(`Failed to persist OAuth extras (localStorage quota exceeded?): ${e instanceof Error?e.message:String(e)}`)}}function se(e,t){let n={input:0,output:0,cacheRead:0,cacheWrite:0},r=ie(e);if(!r)return n;let i;for(let e of t.values()){let t=ie(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 ce=t(`model-policy`),m=`/etc/models`;function h(){return{sections:{}}}function le(e){let t=e.indexOf(`#`);return(t===-1?e:e.slice(0,t)).trim()}function ue(e){let t=h();if(typeof e!=`string`)return t;let n=null;for(let r of e.split(/\r?\n/)){let e=le(r);if(!e)continue;let i=/^\[([^\]]+)\]$/.exec(e);if(i){n=i[1].trim(),t.sections[n]??=[];continue}if(n===null){ce.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){ce.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 g(e,t,n){return e.providerId===t?e.modelId===`*`||e.modelId===n:!1}function de(e,t){return e.sections[t]??[]}function fe(e,t,n,r){return de(e,t).some(e=>e.deny&&g(e,n,r))}function pe(e,t,n,r){let i=de(e,t);return i.some(e=>e.deny&&g(e,n,r))?!1:n===t||i.some(e=>!e.deny&&g(e,n,r))}let me=h();function he(e){me=e}function _(){return me}function ge(e,t,n){return`add \`${t}:${n}\` (or \`${t}:*\`) under \`[${e}]\` in ${m}`}var _e=e({ACCOUNTS_KEY:()=>b,__test__:()=>Ce,addAccount:()=>P,applyProviderDefaults:()=>Ee,clearAllSettings:()=>ut,clearApiKey:()=>at,clearBaseUrl:()=>ct,clearSelectedProvider:()=>nt,ensureOAuthMaskReplica:()=>Je,exportProviders:()=>lt,getAccounts:()=>M,getAllAvailableModels:()=>Ae,getAllExtraOAuthDomains:()=>Fe,getAlternativeModelProviders:()=>je,getApiKey:()=>rt,getApiKeyForProvider:()=>z,getApiVersionForProvider:()=>Qe,getAvailableProviders:()=>w,getBaseUrl:()=>ot,getBaseUrlForProvider:()=>B,getDeploymentForProvider:()=>Ze,getExtraOAuthDomains:()=>Me,getOAuthAccountInfo:()=>k,getProviderConfig:()=>E,getProviderModels:()=>O,getRawApiKeyForProvider:()=>R,getSelectedModelId:()=>V,getSelectedProvider:()=>H,isModelHiddenFromPicker:()=>j,isUsableOAuthMaskReplica:()=>I,logoutOAuthAccount:()=>Re,maskOAuthTokenWithRetry:()=>ze,migrateLegacyAuthOnlySelection:()=>xe,modelRunsOnProvider:()=>ht,persistOAuthMaskViaServiceWorker:()=>L,policyVisible:()=>ke,providerOffersLlmModels:()=>T,removeAccount:()=>F,resolveCurrentModel:()=>K,resolveModelById:()=>G,resolveModelByShorthand:()=>gt,resolveModelIdForScoop:()=>St,resolveModelSelectionForScoop:()=>X,saveOAuthAccount:()=>Xe,setApiKey:()=>it,setBaseUrl:()=>st,setExtraOAuthDomains:()=>Ne,setExtraOAuthDomainsAsync:()=>Pe,setSelectedModelId:()=>$e,setSelectedProvider:()=>tt});const v=ee,y=d,b=`slicc_accounts`,x=`selected-model`,ve=[`slicc_provider`,`slicc_api_key`,`slicc_base_url`,`anthropic_api_key`,`api_provider`,`azure_resource`,`bedrock_region`],ye=new Set([`github`]);function S(e){let t;try{t=u()}catch{return!1}return t.includes(e)&&!a(e)}let C=!1;function be(){if(!C){C=!0;for(let e of ve)try{localStorage.removeItem(e)}catch{}xe()}}function xe(){try{let e=localStorage.getItem(x);if(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t);(ye.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 Se(){C=!1}const Ce={_resetLegacyCleanup:Se};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 we(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=re(B(`bedrock-camp`));return y(`amazon-bedrock`).filter(t=>te(t,e)).map(e=>({...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`,reasoning:e.reasoning===!0&&ne(e)}))}let t=E(e);if(t.getModelIds){let n;try{n=t.getModelIds()}catch(t){return A.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=U(e,n.id,``,a),n.name&&(s.name=n.name),i===`anthropic`){let e=se(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 A.error(`Failed to load models`,{providerId:e,error:t instanceof Error?t.message:String(t)}),[]}}function k(e){let t=M().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 Te=Object.assign({})[`/packages/webapp/providers.json`]??[],A=t(`provider-settings`);function Ee(e=Te){if(e.length===0||M().length>0)return;let t=new Set(w());for(let n of e)if(!(!n.providerId||!n.apiKey)){if(!t.has(n.providerId)){A.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(x)&&localStorage.setItem(x,`${n.providerId}:${n.model}`)}const De=[/haiku/i];function j(e){return De.some(t=>t.test(e))}function Oe(e){return e.filter(e=>!j(e.id))}function ke(e,t){let n=Z();if(n===null)return e;let r=_();return e.filter(e=>!fe(r,n,t,e.id))}function Ae(){let e=M();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=ke(Oe(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 je(e){return Ae().map(e=>e.providerId).filter(t=>t!==e)}function M(){be();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 Me(e){return p(localStorage)[e]??[]}function Ne(e,t){let n=p(localStorage),r=t.map(e=>e.trim()).filter(e=>e.length>0);r.length===0?delete n[e]:n[e]=r,oe(localStorage,n)}async function Pe(e,t){if(r()){Ne(e,t);return}let i=n();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{oe(localStorage,a)}catch(t){A.warn(`worker-shim mirror failed after successful page write — reload to refresh`,{providerId:e,error:t instanceof Error?t.message:String(t)})}}function Fe(){return p(localStorage)}function Ie(e){localStorage.setItem(b,JSON.stringify(e))}async function N(e){if(r()){Ie(e);return}let t=n();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){A.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=M().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),Ie(a)}async function Le(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&&A.error(`SW secrets.delete transport failed`,{providerId:e,error:chrome.runtime.lastError.message}),t(n??{})})});t.error&&A.error(`SW secrets.delete returned error`,{providerId:e,error:t.error})}else if(t===`extension-delegate`){let t=await f(`secrets.delete`,{name:`oauth.${e}.token`});t?.error&&A.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&&A.warn(`OAuth replica DELETE non-ok`,{providerId:e,status:t.status})}}catch(n){A.error(`OAuth replica removal failed`,{providerId:e,topology:t,error:n instanceof Error?n.message:String(n)})}}async function F(e){let t=M().find(t=>t.providerId===e),n=E(e);t&&n?.isOAuth&&await Re(e),await Le(e),await N(M().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 Re(e){let t=M().find(t=>t.providerId===e);if(!t)return;let n=E(e);if(n?.isOAuth){if(n.onOAuthLogout)try{await n.onOAuthLogout()}catch(t){A.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-IlBYPvwr.js`);await t(r).catch(t=>{A.warn(`IdP logout popup failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})})}}await N(M().map(t=>t.providerId===e?{providerId:t.providerId,apiKey:``,baseUrl:t.baseUrl,userName:t.userName,userAvatar:t.userAvatar,loggedOut:!0}:t)),await Le(e)}}async function ze(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 Be=`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}):(A.warn(`OAuth mask write raced with token rotation; discarding replica`,{providerId:e}),{error:Be}):{error:`account gone after mask write`}}function I(e,t){return!!e&&e!==t}async function L(e,t,n){let r={providerId:e.providerId,accessToken:e.accessToken,domains:e.domains.join(`,`)},{maskedValue:i,lastError:a}=await ze(()=>t.sendMaskRequest(r),n);if(!I(i,e.accessToken)){let t=i?`mask replica equals the access token`:a??`no error reported (cold SW or empty reply)`;return A.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=Me(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&&A.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&&A.warn(`SW mask-oauth-token returned error`,{providerId:e.providerId,error:r.error}),t(r??{})})})}function We(e){return f(`secrets.mask-oauth-token`,e).then(t=>(t?.error&&A.warn(`Bridge mask-oauth-token returned error`,{providerId:e.providerId,error:t.error}),t??{})).catch(t=>(A.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 A.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 A.warn(`OAuth replica POST ok but missing maskedValue`,{providerId:e,url:r}),{error:`OAuth replica POST ok but missing maskedValue`};if(!I(a.maskedValue,t))return A.error(`OAuth mask replica equals the access token; refusing to persist`,{providerId:e}),{error:`mask replica equals the access token`};let o=M(),l=Ve(e,t,a.maskedValue,o);return l.maskedValue&&await N(o),l}async function Ke(e,t){let n=await qe(e,t);if(n.error!==Be)return n;let r=M().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`?L({providerId:e,accessToken:t,domains:n},{sendMaskRequest:Ue,getAccounts:M,saveAccounts:N}):r===`extension-delegate`?L({providerId:e,accessToken:t,domains:n},{sendMaskRequest:We,getAccounts:M,saveAccounts:N}):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 A.error(`OAuth replica sync failed`,{providerId:e,topology:r,error:n}),{error:n}}}async function Je(e){let t=M().find(t=>t.providerId===e);return t?.accessToken?I(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=M().find(t=>t.providerId===e.providerId),n=M().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 N(n),await Ke(e.providerId,e.accessToken)}function R(e){let t=M().find(t=>t.providerId===e);return t&&(t.accessToken||t.apiKey)||null}function z(e){let t=M().find(t=>t.providerId===e);return t?t.accessToken||t.apiKey||(E(e).optionalApiKey?`local`:null):null}function B(e){return M().find(t=>t.providerId===e)?.baseUrl??null}function Ze(e){return M().find(t=>t.providerId===e)?.deployment??null}function Qe(e){return M().find(t=>t.providerId===e)?.apiVersion??null}function V(){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=H();localStorage.setItem(x,`${n}:${e}`)}function et(){return localStorage.getItem(x)||``}function H(){let e=et(),t=e.indexOf(`:`);if(t>0)return e.slice(0,t);let n=M(),r=V();if(r){let e=n.find(e=>T(e.providerId)&&we(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=V();localStorage.setItem(x,`${e}:${t}`)}function nt(){let e=V();localStorage.setItem(x,e)}function rt(){return z(H())}function it(e){let t=H();P(t,e,B(t)??void 0)}async function at(){await F(H())}function ot(){return B(H())}function st(e){let t=H(),n=R(t);n&&P(t,n,e||void 0)}function ct(){let e=H(),t=R(e);t&&P(e,t)}function lt(){let e=M(),t=H(),n=V();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=M();await Promise.allSettled(e.map(e=>F(e.providerId))),localStorage.removeItem(b),localStorage.removeItem(x);for(let e of ve)localStorage.removeItem(e)}function dt(e){return/^(?:gpt[-.]?|o[0-9]|chatgpt)/i.test(e)?`openai`:`anthropic`}function U(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 W(e,t){return t.isOAuth||e===`azure-ai-foundry`?`anthropic`:e===`bedrock-camp`?`amazon-bedrock`:e}function ft(e,t,n){let r=O(e).find(e=>e.id===t);return r?n?{...r,baseUrl:n}:r:null}function pt(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 mt(e,t,n,r,i){if(t.isOAuth)return ft(e,n,r)??U(e,n,r);if(i)throw Error(`Model ${n} is not available from provider ${e}`);return K()}function G(e,t){if(!e)return K();let n=t??H(),r=B(n),i=E(n);if(t!==void 0){let t=ft(n,e,r);if(t)return t}try{let t=W(n,i),a=v(t,e);if(!a?.id)throw Error(`Model ${e} not found`);let o=pt(a,n,i,e);return r?{...o,baseUrl:r}:o}catch(a){return A.debug(`resolveModelById: pi-ai lookup miss, using provider fallback`,{providerId:n,modelId:e,error:a instanceof Error?a.message:String(a)}),mt(n,i,e,r,t!==void 0)}}function ht(e,t){return e.provider===t||e.provider===W(t,E(t))}function K(){let e=H(),t=V(),n=B(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=W(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){A.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?U(e,o,n):v(`anthropic`,`claude-sonnet-4-0`)}}function gt(e){let t=e.toLowerCase();if(!t)return null;let n=null;try{n=H()}catch{}if(n!==null){let e=q(t,[n]);if(e!==null)return e}return q(t,M().map(e=>e.providerId))}function q(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&&Ct(t.id,n??``)>0)&&(r=o,n=t.id)}return n}function J(){let e=[];try{for(let t of M())e.includes(t.providerId)||e.push(t.providerId)}catch{}try{let t=H();e.includes(t)||e.push(t)}catch{}return e}function _t(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=J().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 Y(e,t,n=!0){let r=[e],i=n?q(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(G(n,t).id===n)return{modelId:n,providerId:t}}catch(r){A.debug(`selectModelFromProvider: candidate did not resolve`,{input:e,candidate:n,providerId:t,error:r instanceof Error?r.message:String(r)})}return null}function vt(e){return e.map(e=>`${e.providerId}:${e.modelId}`).join(`, `)}function X(e){if(!e)return{ok:!1,error:`unknown model: (empty)`};let t=_t(e);if(t)return bt(e,t);let n=null;try{n=H()}catch{}if(n!==null){let t=Y(e,n);if(t)return $(t)}return xt(e,n)}function Z(){try{return H()}catch{return null}}function Q(e){let t=Z();return t===null||pe(_(),t,e.providerId,e.modelId)}function yt(e){let t=Z()??`<none>`;return{ok:!1,error:`model not allowed: ${`${e.providerId}:${e.modelId}`} is blocked by ${m} while "${t}" is selected — ${ge(t,e.providerId,e.modelId)}`}}function $(e){return Q(e)?{ok:!0,selection:e}:yt(e)}function bt(e,t){if(!t.configured){let n=J();return{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" is not configured${n.length>0?`; configured: ${n.join(`, `)}`:``})`}}let n=Y(t.modelId,t.providerId);return n?$(n):{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" does not offer "${t.modelId}")`}}function xt(e,t){let n=J().filter(e=>e!==t);for(let t of[`exact`,`shorthand`]){let r=[];for(let i of n){let n=t===`exact`?e:q(e.toLowerCase(),[i]);if(n===null)continue;let a=Y(n,i,!1);a&&r.push(a)}let i=r.filter(Q);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 ${vt(i)} — qualify it as provider:model`}}return{ok:!1,error:`unknown model: ${e}`}}function St(e){let t=X(e);return t.ok?t.selection.modelId:null}function Ct(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{se as S,Xe as _,z as a,ue as b,Ze as c,V as d,H as f,X as g,G as h,rt as i,k as l,K as m,Je as n,Qe as o,ht as p,M as r,B as s,_e as t,O as u,m as v,he as x,h as y};
|