sliccy 6.108.2 → 6.110.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/.vite/manifest.json +120 -120
- package/dist/ui/assets/{MEMORY-CJ-KH_KB.js → MEMORY-BEEBDXkY.js} +2 -2
- package/dist/ui/assets/{SKILL-FnBxPcDX.js → SKILL-Br-bRfNe.js} +1 -1
- package/dist/ui/assets/{SKILL-D6a6ZdMA.js → SKILL-CcQMVElP.js} +2 -2
- package/dist/ui/assets/SKILL-DpjPj8Lx.js +1 -0
- package/dist/ui/assets/{SKILL-DMQtY0uF.js → SKILL-I-bFttZa.js} +4 -4
- package/dist/ui/assets/{SKILL-Ch-cOl61.js → SKILL-WFB8Hz-G.js} +1 -1
- package/dist/ui/assets/SKILL-ezqO98T2.js +1 -0
- package/dist/ui/assets/{account-store-BoOQBxq_.js → account-store-C3hpxUnj.js} +1 -1
- package/dist/ui/assets/{account-store-Ds_fREWW.js → account-store-DmTUzOwD.js} +1 -1
- package/dist/ui/assets/{adobe-BmQmmCZ8.js → adobe-C6blUXjx.js} +1 -1
- package/dist/ui/assets/{adobe-GJcl8jyr.js → adobe-CeRHOrV3.js} +1 -1
- package/dist/ui/assets/{agent-bridge-Dvaymghq.js → agent-bridge-BytMhrHG.js} +1 -1
- package/dist/ui/assets/{agent-panels-BFW07lEI.js → agent-panels-CW3VQhe5.js} +1 -1
- package/dist/ui/assets/{apply-layout-iwhCpuVk.js → apply-layout-C9oYfek3.js} +1 -1
- package/dist/ui/assets/{apps-BoUfLAvt.js → apps-buLjOZpb.js} +1 -1
- package/dist/ui/assets/{azure-openai-BeN2uYBT.js → azure-openai-BQJFNoBY.js} +1 -1
- package/dist/ui/assets/{azure-openai-Da41DiKw.js → azure-openai-BvVWJJJ0.js} +1 -1
- package/dist/ui/assets/{cerebras-C0WuimUy.js → cerebras-BjJmr6-W.js} +1 -1
- package/dist/ui/assets/{cerebras-B9eiHzVB.js → cerebras-CokNYO8b.js} +1 -1
- package/dist/ui/assets/{connect-surface-Dwe3iCG6.js → connect-surface-BHqqK_uZ.js} +1 -1
- package/dist/ui/assets/{directed-approval-BAqNNODV.js → directed-approval-CGapNvBZ.js} +1 -1
- package/dist/ui/assets/{fs-aj1JzF11.js → fs-D9DlVz0g.js} +2 -2
- package/dist/ui/assets/{fs-BGQSFWUb.js → fs-b7tPDGDe.js} +1 -1
- package/dist/ui/assets/{github-BngLMnjD.js → github-CAYwnl-b.js} +2 -2
- package/dist/ui/assets/{github-copilot-DzH-Yvn-.js → github-copilot-BaEPoc2l.js} +1 -1
- package/dist/ui/assets/{github-copilot-BBKHa3N_.js → github-copilot-BaohZB3G.js} +1 -1
- package/dist/ui/assets/{github-install-CjSc36VN.js → github-install-9ynDwgiQ.js} +1 -1
- package/dist/ui/assets/{github-WdNypjl-.js → github-rMrWcSVm.js} +1 -1
- package/dist/ui/assets/{github-zip-BmQLkPpT.js → github-zip-nBMk_5yk.js} +1 -1
- package/dist/ui/assets/{js-realm-worker-4r4THCXO.js → js-realm-worker-adzmez5_.js} +2 -2
- package/dist/ui/assets/{jsh-runtime-extensions-DO5wCezb.js → jsh-runtime-extensions-DsXdiFrH.js} +1 -1
- package/dist/ui/assets/{kernel-worker-BR-upeev.js → kernel-worker-DqzdssFu.js} +562 -562
- package/dist/ui/assets/{local-llm-BwOFno6K.js → local-llm--yLtHAZU.js} +1 -1
- package/dist/ui/assets/{local-llm-DLogN3_D.js → local-llm-CiMne0-B.js} +1 -1
- package/dist/ui/assets/{main-ChriOijJ.js → main-CfZo7HyV.js} +3 -3
- package/dist/ui/assets/{mount-dz--0GUQ.js → mount-CC0djJXQ.js} +1 -1
- package/dist/ui/assets/{mount-Djy3MuFR.js → mount-rYO-8m8f.js} +2 -2
- package/dist/ui/assets/{mount-recovery-BrxSEJj1.js → mount-recovery-CvuTQnTL.js} +1 -1
- package/dist/ui/assets/{new-session-nNJq82qZ.js → new-session-BS3W0s9e.js} +2 -2
- package/dist/ui/assets/{oauth-bootstrap-Ck44WkJd.js → oauth-bootstrap-DpG29VCH.js} +2 -2
- package/dist/ui/assets/{onboarding-orchestrator-tYc9g5GB.js → onboarding-orchestrator-BI0_TR0Z.js} +1 -1
- package/dist/ui/assets/{openai-codex-CsaGGbnG.js → openai-codex-BJ4JLVfo.js} +1 -1
- package/dist/ui/assets/{openai-codex-CBzoVUdw.js → openai-codex-C8aOD0t8.js} +1 -1
- package/dist/ui/assets/{openrouter-DqJEt7IP.js → openrouter-DHg1gMnT.js} +1 -1
- package/dist/ui/assets/{openrouter-BoyWG207.js → openrouter-LCca2Pyd.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-BBeVm7Ug.js → openrouter-oauth-CRVMrh7E.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-CH_oprz-.js → openrouter-oauth-DukR2YjX.js} +1 -1
- package/dist/ui/assets/{panel-rpc-handlers-WWK0tVp7.js → panel-rpc-handlers-p0z9KK9s.js} +2 -2
- package/dist/ui/assets/{panelize-shell-DHEUapCc.js → panelize-shell-DRCqD-hf.js} +1 -1
- package/dist/ui/assets/{provider-Dx4_SDNT.js → provider-8X1TXGOz.js} +1 -1
- package/dist/ui/assets/{provider-Ck4Av-1p.js → provider-CMYr90tG.js} +2 -2
- package/dist/ui/assets/{provider-settings-BS6vPXws.js → provider-settings-BbKZBkmj.js} +1 -1
- package/dist/ui/assets/provider-store-access-D5VrJP0f.js +1 -0
- package/dist/ui/assets/provider-store-access-DxHrtPBD.js +1 -0
- package/dist/ui/assets/{providers-DvmKM7--.js → providers-B3XsxeEV.js} +2 -2
- package/dist/ui/assets/{providers-95TyImtj.js → providers-DuNEVEmM.js} +1 -1
- package/dist/ui/assets/{quick-llm-BNah0Kkf.js → quick-llm-BQeXAXhA.js} +1 -1
- package/dist/ui/assets/{recovery-screen-D-pkj9XQ.js → recovery-screen-Dg10xiZK.js} +1 -1
- package/dist/ui/assets/{run-B5AwO0rT.js → run-74Dp3xqK.js} +1 -1
- package/dist/ui/assets/{setup-feature-flags-remote-BlbWNliP.js → setup-feature-flags-remote-BDXotdTe.js} +1 -1
- package/dist/ui/assets/sidecar-repair-Mh9rQVbp.js +1 -0
- package/dist/ui/assets/{skills-DUU7N5bl.js → skills-fw01LxeE.js} +1 -1
- package/dist/ui/assets/slicc-version-DF86HAGG.js +1 -0
- package/dist/ui/assets/{state-R7D031UZ.js → state-YABVHFPR.js} +1 -1
- package/dist/ui/assets/{store-BehdBrM6.js → store--DLIZTf_.js} +1 -1
- package/dist/ui/assets/{store-CWUoE4oC.js → store-DuMDTOYq.js} +1 -1
- package/dist/ui/assets/{sudo-BRlt30iG.js → sudo-Lv8DoXxd.js} +1 -1
- package/dist/ui/assets/{sync-dialog-DF3OUuUL.js → sync-dialog-B8ssqjz4.js} +1 -1
- package/dist/ui/assets/tab-teleport-ChkQ1rYc.js +1 -0
- package/dist/ui/assets/{tray-sidecar-DU4bBWmt.js → tray-sidecar-B1xCDlFe.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-DmRBomaj.js → upgrade-detection-TlqXRH1-.js} +1 -1
- package/dist/ui/assets/{voice-reply-B0lZhnaR.js → voice-reply-CGJnZqW1.js} +1 -1
- package/dist/ui/assets/{wc-attach-DvkHwEWE.js → wc-attach-DANq4_X7.js} +2 -2
- package/dist/ui/assets/{wc-browser-BTGpaihO.js → wc-browser-CYph9bTu.js} +1 -1
- package/dist/ui/assets/{wc-extension-EnN-csY7.js → wc-extension-BRZxL5Z1.js} +2 -2
- package/dist/ui/assets/{wc-float-label-DQprtoDu.js → wc-float-label-KQOIpgnm.js} +1 -1
- package/dist/ui/assets/{wc-floatbar-online-qI6TtF8r.js → wc-floatbar-online-D1LOCURF.js} +1 -1
- package/dist/ui/assets/{wc-follower-C3QyijjU.js → wc-follower-D7D0OBf3.js} +3 -3
- package/dist/ui/assets/{wc-follower-oauth-BH_z0NbH.js → wc-follower-oauth-DZllVzrT.js} +1 -1
- package/dist/ui/assets/{wc-live-iFtSSLXZ.js → wc-live-RnON2Ugk.js} +8 -8
- package/dist/ui/assets/{wc-nav-DDUPTHtU.js → wc-nav-Clor2VFZ.js} +2 -2
- package/dist/ui/assets/{wc-onboarding-Dv3aqS2R.js → wc-onboarding-BUmFCg_U.js} +2 -2
- package/dist/ui/assets/{wc-placeholder-BbcvMQCq.js → wc-placeholder-8Judy9cG.js} +3 -3
- package/dist/ui/assets/{wc-settings-D0_CDKNP.js → wc-settings-Bvx2BLQv.js} +3 -3
- package/dist/ui/assets/{wc-shell-Ce7_gK_H.js → wc-shell-CsfCsd9q.js} +2 -2
- package/dist/ui/assets/{wc-shortcut-config-DMeDhvME.js → wc-shortcut-config-BK4O1f_G.js} +1 -1
- package/dist/ui/assets/{wc-sprinkles-D_3FmUg9.js → wc-sprinkles-D65r5HD-.js} +2 -2
- package/dist/ui/assets/{wc-transcript-export-BvJ0t1kU.js → wc-transcript-export-UUnE-lk6.js} +2 -2
- package/dist/ui/assets/{wc-tray-CaDDIPMX.js → wc-tray-CPQsc5Us.js} +4 -4
- package/dist/ui/assets/welcome-detection-D7QFjBKz.js +1 -0
- package/dist/ui/assets/{xai-grok-BaQCEEMQ.js → xai-grok-DoYWlKs_.js} +1 -1
- package/dist/ui/assets/{xai-grok-DGbYjl5N.js → xai-grok-IHkw5Jqa.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-DP0GgPyN.js +0 -1
- package/dist/ui/assets/SKILL-DsJ4mys3.js +0 -1
- package/dist/ui/assets/provider-store-access-BEgIBo-1.js +0 -1
- package/dist/ui/assets/provider-store-access-DkCFeqO2.js +0 -1
- package/dist/ui/assets/sidecar-repair-QunFTxmC.js +0 -1
- package/dist/ui/assets/slicc-version-BZU-hOf1.js +0 -1
- package/dist/ui/assets/tab-teleport-CLc1Tuqi.js +0 -1
- package/dist/ui/assets/welcome-detection-VnqBEB4J.js +0 -1
|
@@ -109,7 +109,7 @@ Rules:
|
|
|
109
109
|
|
|
110
110
|
- Keep durable preferences, stable project facts, validated approaches, named resources, and the pitfalls worth not repeating.
|
|
111
111
|
- Organize retained information into concise per-topic sections rather than one flat list. Let the topic lead the heading; preferences, projects and pitfalls are what to look for, not a required table of contents.
|
|
112
|
-
- Never write next to the memory file. It is versioned, so backups and scratch copies beside it are noise; drafts belong in \`{{SCRATCH_DIR}}\`. Do not draft in \`/tmp/\` either
|
|
112
|
+
- Never write next to the memory file. It is versioned, so backups and scratch copies beside it are noise; drafts belong in \`{{SCRATCH_DIR}}\`. Do not draft in the shared \`/tmp/\` root either — every other unit can read and overwrite what you leave there. \`$TMPDIR\` is your own directory under it and is the right home for ordinary scratch, but a full rewrite of durable memory still belongs in \`{{SCRATCH_DIR}}\`, which is private to the run and removed when the pass ends.
|
|
113
113
|
- Shell tools only. Interpreters such as \`python3\` and \`node -e\` are not on your allow-list, so reaching for one costs an approval round-trip and may fail even when approved — \`awk\`, \`sed\`, \`wc\` and \`sort\` cover every measurement this pass needs.
|
|
114
114
|
- End every \`##\` and \`###\` section heading with its last-verified date in \`YYYY-MM-DD\` form, for example \`## Deployment pipeline (2026-08-06)\`. Dates are UTC, matching the session archive timestamps, so a late-evening freeze west of UTC stamps the next day.
|
|
115
115
|
- Stamp sections you write or confirm with today's date, {{TODAY}}.
|
|
@@ -126,7 +126,7 @@ Add curator instructions here, for example: also update the knowledge base at /p
|
|
|
126
126
|
|
|
127
127
|
writablePaths defaults to the memory file alone rather than /workspace/, because the curator can run upskill and a directory-wide grant would also let it install skills into /workspace/skills. Widen it only as far as a task genuinely needs; a single file is a valid entry, not just a directory.
|
|
128
128
|
|
|
129
|
-
Scratch space needs no entry here. The curator spawns under a fixed per-cone name (memory-curator for the primary cone), so the bridge grants it {{SCRATCH_DIR}} — private to the run, writable without a prompt, and removed when the pass ends — and that is where the prompt sends drafts.
|
|
129
|
+
Scratch space needs no entry here. The curator spawns under a fixed per-cone name (memory-curator for the primary cone), so the bridge grants it {{SCRATCH_DIR}} — private to the run, writable without a prompt, and removed when the pass ends — and that is where the prompt sends drafts. $TMPDIR is writable too and is this unit's own directory, but it outlives the run and stays readable by the cone, so a full rewrite of durable memory does not belong there.
|
|
130
130
|
|
|
131
131
|
thinkingLevel accepts off, minimal, low, medium, high, or xhigh. Curation is cheaper with reasoning than without: an unreasoned pass converges on the budget by trial and error, and because every turn re-reads the whole context, turn count is what the pass costs. Lower it to off only if you also shrink the prompt to a single mechanical instruction.
|
|
132
132
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e="---\nname: dips\ndescription: |\n Use this whenever a response could benefit from richer visualization, guided\n interaction, or a touch of fun — pickers, calculators, sliders, mini\n explorers, charts, animated demos, choose-your-own-adventure prompts,\n anything that lands better as a hydrating widget than as plain prose. Dips\n are ephemeral `shtml` code blocks rendered inline in chat (no state\n persistence, lick-only). Reach for them generously: every interactive moment\n the user gets is a moment they don't have to type a clarifying message. For\n persistent dashboards, editors, or multi-page apps use sprinkles instead.\nallowed-tools: bash\n---\n\n# Dips\n\nDips are inline `shtml` code blocks in chat that hydrate into sandboxed interactive widgets. **Ephemeral** — no state persistence, no `readFile`. Only `slicc.lick(event)` is available for agent communication; always pack payloads as `{ action, data: { ... } }` for a predictable shape on the cone side.\n\n**Use them generously.** A dip is the right answer any time a response could benefit from visualization, interaction, or a moment of delight. Don't reserve them for \"complex\" tasks — a slider that lets the user feel a number, a chart that beats a paragraph of stats, a button that's faster than typing \"yes\" all earn their keep.\n\n| Reach for a dip when … | Reach for a sprinkle when … |\n| ------------------------------------------------------ | --------------------------------------------------------------- |\n| The answer would land better as a widget than as prose | The user needs a persistent dashboard / editor / multi-page app |\n| The user is choosing, confirming, or tuning a value | The UI must survive across turns or sessions |\n| You want a chart, animation, or interactive demo | You need `readFile`, `screenshot`, or any persistent state |\n| The result deserves a bit of fun | The interaction is long-running |\n\nFor a gallery of 10 ready-to-adapt patterns (drag-on-canvas, slider→DOM reflow, paste→tree, ...) read the companion file: `read_file /workspace/skills/dips/patterns.md`.\n\n## Card structure\n\nAlways wrap interactive content in `.sprinkle-action-card` for visual containment. Don't output bare HTML — cards need a visible boundary.\n\n```shtml\n<div class=\"sprinkle-action-card\">\n <div class=\"sprinkle-action-card__header\">Title <span class=\"sprinkle-badge sprinkle-badge--notice\">Status</span></div>\n <div class=\"sprinkle-action-card__body\">Description text</div>\n <div class=\"sprinkle-action-card__actions\">\n <button class=\"sprinkle-btn sprinkle-btn--secondary\" onclick=\"slicc.lick('cancel')\">Cancel</button>\n <button class=\"sprinkle-btn sprinkle-btn--primary\" onclick=\"slicc.lick('confirm')\">Confirm</button>\n </div>\n</div>\n```\n\nWhen the user clicks a button, you receive the lick as a message. Respond conversationally, with another dip, or by spawning a scoop.\n\n### Use existing components inside cards\n\n- **Progress** → `.sprinkle-progress-bar` with `--progress` CSS variable. Not raw colored divs.\n- **Status indicators** → `.sprinkle-status-light` with `--positive` / `--negative` / `--notice` variants.\n- **Stats** → `.sprinkle-stat-card` grid.\n- **Tables** → `.sprinkle-table` with `.sprinkle-badge` for severity.\n- **Badges** → `.sprinkle-badge` variants (`--positive`, `--negative`, `--notice`, `--informative`).\n\nMultiple cards in one message: each is a separate `.sprinkle-action-card`. The iframe padding handles spacing. Don't wrap multiple cards in a single container.\n\n## Pre-styled form elements\n\nInside dips, bare HTML form elements are pre-styled to match S2:\n\n- `<input type=\"range\">` — 4px track, 18px accent thumb.\n- `<input type=\"text\">`, `<textarea>` — S2 layer-2 background, accent focus ring. `rows` is fine as an initial size; never add `overflow:auto` or a fixed pixel height — the dip auto-sizes (see \"Sizing\").\n- `<select>` — S2 styled with focus ring.\n- `<button>` — pill-rounded, 28px height, hover state. Use `.sprinkle-btn--primary` class for the accent fill.\n- `<canvas>` — full-width, rounded corners.\n- `<mark>` — accent-tinted highlight.\n\nNo custom CSS needed for basic widgets. Just write bare HTML.\n\n## Sizing\n\nThe dip iframe auto-sizes to its content via `ResizeObserver` on `document.body`. **Your content's natural height becomes the iframe height. Don't fight it.**\n\nNever set `height`, `max-height`, `min-height`, `overflow: auto`, or `overflow: scroll` on dip containers. The outer iframe has `overflow: hidden`, so any internal scroller or fixed height clips content instead of growing the iframe. Let content flow and grow.\n\nThe one exception is `<canvas>`: canvases need explicit pixel `width`/`height` for the drawing buffer. That's a drawing surface, not a scrollable container — set the pixel dimensions you need.\n\nIf content is long enough that it would want to scroll, it belongs in a sprinkle.\n\n## Light & dark mode\n\nDips inherit the parent's theme automatically. The parent injects its S2 tokens and toggles a `.theme-light` class on the iframe's `<html>` whenever the user flips themes; every `var(--s2-*)` token swaps in lockstep. **Never hard-code dark colors** — always use S2 tokens, or `light-dark()` for one-off custom colors (set `color-scheme: light dark` on an ancestor). **Do not use `@media (prefers-color-scheme: ...)`** — the parent toggle is class-based, so media queries desync. See `/workspace/skills/sprinkles/style-guide.md` for the full S2 token reference and a `light-dark()` example.\n\n## Color palette for charts\n\nUse these classes on chart elements, diagram nodes, or badges. Two or three colors per visualization, not six. Assign by meaning, not by sequence.\n\n| Class | Use for |\n| ----------- | ----------------------- |\n| `.c-purple` | Primary category, AI/ML |\n| `.c-teal` | Success, growth |\n| `.c-coral` | Secondary category |\n| `.c-pink` | Tertiary category |\n| `.c-gray` | Neutral, structural |\n| `.c-blue` | Informational |\n| `.c-amber` | Warning, in-progress |\n| `.c-red` | Error, critical |\n| `.c-green` | Success, complete |\n\n## Layout & viewport\n\nDips render in the chat column. They are **single-column by design** — don't author multi-column or sidebar+main layouts. The chat column is narrow on iOS / Sliccstart and on the extension side panel; multi-column dips break visually in those floats. If you need multi-column, use a sprinkle and let the user pop it to full-screen.\n\n## Cheap interactions via `agent`\n\nWhen a dip's lick handler needs to do real work (lookup, transformation, generation) but the cone or owning scoop should NOT be woken up, route the lick to a one-shot `agent` call.\n\nThe flow:\n\n1. Dip emits a lick: `slicc.lick({action: 'compute', data: {input: ...}})`.\n2. The lick reaches the cone (or scoop) as a message.\n3. Instead of replying conversationally, the receiver shells out to `agent` with a tight allow-list and a self-contained prompt.\n4. `agent` returns the answer on stdout. The receiver writes the result back to the dip via a follow-up dip or via `sprinkle send` (if the dip was the entry point to a sprinkle flow).\n\n```bash\n# Inside a feed_scoop reply to a dip lick:\nresult=$(agent /tmp \"curl,jq\" \"Fetch <url>, return field 'price' as a number.\")\n# Render a fresh dip with `result` inline.\n```\n\n`agent` is the right tool here because it has **no handoff**: ephemeral scoops don't notify the cone on completion, so a busy cone or sprinkle-owning scoop isn't pre-empted by every dip click. See `/workspace/skills/delegation/SKILL.md` for the full `agent` reference.\n\n## Design rules\n\n- Use S2 CSS variables for all colors (`var(--s2-content-default)`, `var(--s2-bg-layer-2)`, ...).\n- Round all displayed numbers: `Math.round()`, `.toFixed(2)`, `.toLocaleString()`.\n- Set `step` on range sliders for clean values.\n- Show errors inline (not `alert()`).\n- Sentence case always — never Title Case or ALL CAPS.\n- One root `render()` / `calc()` function triggered by all inputs.\n- Call `render()` on load with defaults so the widget is immediately interactive.\n\n## Don't\n\n- Don't hardcode hex colors or theme-specific values — use S2 tokens or `light-dark()` (see \"Light & dark mode\").\n- Don't use `@media (prefers-color-scheme: ...)` to swap colors — the parent's theme toggle is class-based and the media query desyncs.\n- Don't set `height`, `max-height`, `min-height`, `overflow: auto`, or `overflow: scroll` on dip containers — the iframe auto-sizes to content; fixed heights and internal scrollers clip it. Canvases are the one exception (they need pixel dimensions for the drawing buffer). Long content belongs in a sprinkle.\n- Don't build custom progress bars — use `.sprinkle-progress-bar`.\n- Don't build custom status dots — use `.sprinkle-status-light`.\n- Don't use numbered headings (1. File Operations) inside cards — the `__header` IS the heading.\n- Don't put prose / markdown headings between cards — let cards stand alone.\n\n## Lick patterns\n\n```javascript\n// Explicit \"send to agent\" button.\nslicc.lick({ action: 'use-config', data: { config: getCurrentConfig() } });\n\n// Lick on threshold crossing.\nif (total > budget) {\n slicc.lick({ action: 'over-budget', data: { total, budget, breakdown } });\n}\n\n// Lick on completion.\nslicc.lick({ action: 'sort-complete', data: { algorithm: algo, comparisons: n } });\n```\n\n> **Payload shape:** `slicc.lick(eventOrAction)` accepts either a plain\n> action string (`slicc.lick('cancel')`) or an `{ action, data? }`\n> object. **Always pack extra fields inside `data: { ... }`** — the\n> cone reads `event.data` as the payload, so top-level extras either\n> get dropped (sprinkle bridge) or folded into `data` as the whole\n> event object (dip bridge), neither of which gives the cone a\n> predictable shape. The canonical form works identically in both.\n\nThe agent receives the lick as a structured message and can respond with prose, another dip, or spawn a scoop.\n";export{e as default};
|
|
1
|
+
var e="---\nname: dips\ndescription: |\n Use this whenever a response could benefit from richer visualization, guided\n interaction, or a touch of fun — pickers, calculators, sliders, mini\n explorers, charts, animated demos, choose-your-own-adventure prompts,\n anything that lands better as a hydrating widget than as plain prose. Dips\n are ephemeral `shtml` code blocks rendered inline in chat (no state\n persistence, lick-only). Reach for them generously: every interactive moment\n the user gets is a moment they don't have to type a clarifying message. For\n persistent dashboards, editors, or multi-page apps use sprinkles instead.\nallowed-tools: bash\n---\n\n# Dips\n\nDips are inline `shtml` code blocks in chat that hydrate into sandboxed interactive widgets. **Ephemeral** — no state persistence, no `readFile`. Only `slicc.lick(event)` is available for agent communication; always pack payloads as `{ action, data: { ... } }` for a predictable shape on the cone side.\n\n**Use them generously.** A dip is the right answer any time a response could benefit from visualization, interaction, or a moment of delight. Don't reserve them for \"complex\" tasks — a slider that lets the user feel a number, a chart that beats a paragraph of stats, a button that's faster than typing \"yes\" all earn their keep.\n\n| Reach for a dip when … | Reach for a sprinkle when … |\n| ------------------------------------------------------ | --------------------------------------------------------------- |\n| The answer would land better as a widget than as prose | The user needs a persistent dashboard / editor / multi-page app |\n| The user is choosing, confirming, or tuning a value | The UI must survive across turns or sessions |\n| You want a chart, animation, or interactive demo | You need `readFile`, `screenshot`, or any persistent state |\n| The result deserves a bit of fun | The interaction is long-running |\n\nFor a gallery of 10 ready-to-adapt patterns (drag-on-canvas, slider→DOM reflow, paste→tree, ...) read the companion file: `read_file /workspace/skills/dips/patterns.md`.\n\n## Card structure\n\nAlways wrap interactive content in `.sprinkle-action-card` for visual containment. Don't output bare HTML — cards need a visible boundary.\n\n```shtml\n<div class=\"sprinkle-action-card\">\n <div class=\"sprinkle-action-card__header\">Title <span class=\"sprinkle-badge sprinkle-badge--notice\">Status</span></div>\n <div class=\"sprinkle-action-card__body\">Description text</div>\n <div class=\"sprinkle-action-card__actions\">\n <button class=\"sprinkle-btn sprinkle-btn--secondary\" onclick=\"slicc.lick('cancel')\">Cancel</button>\n <button class=\"sprinkle-btn sprinkle-btn--primary\" onclick=\"slicc.lick('confirm')\">Confirm</button>\n </div>\n</div>\n```\n\nWhen the user clicks a button, you receive the lick as a message. Respond conversationally, with another dip, or by spawning a scoop.\n\n### Use existing components inside cards\n\n- **Progress** → `.sprinkle-progress-bar` with `--progress` CSS variable. Not raw colored divs.\n- **Status indicators** → `.sprinkle-status-light` with `--positive` / `--negative` / `--notice` variants.\n- **Stats** → `.sprinkle-stat-card` grid.\n- **Tables** → `.sprinkle-table` with `.sprinkle-badge` for severity.\n- **Badges** → `.sprinkle-badge` variants (`--positive`, `--negative`, `--notice`, `--informative`).\n\nMultiple cards in one message: each is a separate `.sprinkle-action-card`. The iframe padding handles spacing. Don't wrap multiple cards in a single container.\n\n## Pre-styled form elements\n\nInside dips, bare HTML form elements are pre-styled to match S2:\n\n- `<input type=\"range\">` — 4px track, 18px accent thumb.\n- `<input type=\"text\">`, `<textarea>` — S2 layer-2 background, accent focus ring. `rows` is fine as an initial size; never add `overflow:auto` or a fixed pixel height — the dip auto-sizes (see \"Sizing\").\n- `<select>` — S2 styled with focus ring.\n- `<button>` — pill-rounded, 28px height, hover state. Use `.sprinkle-btn--primary` class for the accent fill.\n- `<canvas>` — full-width, rounded corners.\n- `<mark>` — accent-tinted highlight.\n\nNo custom CSS needed for basic widgets. Just write bare HTML.\n\n## Sizing\n\nThe dip iframe auto-sizes to its content via `ResizeObserver` on `document.body`. **Your content's natural height becomes the iframe height. Don't fight it.**\n\nNever set `height`, `max-height`, `min-height`, `overflow: auto`, or `overflow: scroll` on dip containers. The outer iframe has `overflow: hidden`, so any internal scroller or fixed height clips content instead of growing the iframe. Let content flow and grow.\n\nThe one exception is `<canvas>`: canvases need explicit pixel `width`/`height` for the drawing buffer. That's a drawing surface, not a scrollable container — set the pixel dimensions you need.\n\nIf content is long enough that it would want to scroll, it belongs in a sprinkle.\n\n## Light & dark mode\n\nDips inherit the parent's theme automatically. The parent injects its S2 tokens and toggles a `.theme-light` class on the iframe's `<html>` whenever the user flips themes; every `var(--s2-*)` token swaps in lockstep. **Never hard-code dark colors** — always use S2 tokens, or `light-dark()` for one-off custom colors (set `color-scheme: light dark` on an ancestor). **Do not use `@media (prefers-color-scheme: ...)`** — the parent toggle is class-based, so media queries desync. See `/workspace/skills/sprinkles/style-guide.md` for the full S2 token reference and a `light-dark()` example.\n\n## Color palette for charts\n\nUse these classes on chart elements, diagram nodes, or badges. Two or three colors per visualization, not six. Assign by meaning, not by sequence.\n\n| Class | Use for |\n| ----------- | ----------------------- |\n| `.c-purple` | Primary category, AI/ML |\n| `.c-teal` | Success, growth |\n| `.c-coral` | Secondary category |\n| `.c-pink` | Tertiary category |\n| `.c-gray` | Neutral, structural |\n| `.c-blue` | Informational |\n| `.c-amber` | Warning, in-progress |\n| `.c-red` | Error, critical |\n| `.c-green` | Success, complete |\n\n## Layout & viewport\n\nDips render in the chat column. They are **single-column by design** — don't author multi-column or sidebar+main layouts. The chat column is narrow on iOS / Sliccstart and on the extension side panel; multi-column dips break visually in those floats. If you need multi-column, use a sprinkle and let the user pop it to full-screen.\n\n## Cheap interactions via `agent`\n\nWhen a dip's lick handler needs to do real work (lookup, transformation, generation) but the cone or owning scoop should NOT be woken up, route the lick to a one-shot `agent` call.\n\nThe flow:\n\n1. Dip emits a lick: `slicc.lick({action: 'compute', data: {input: ...}})`.\n2. The lick reaches the cone (or scoop) as a message.\n3. Instead of replying conversationally, the receiver shells out to `agent` with a tight allow-list and a self-contained prompt.\n4. `agent` returns the answer on stdout. The receiver writes the result back to the dip via a follow-up dip or via `sprinkle send` (if the dip was the entry point to a sprinkle flow).\n\n```bash\n# Inside a feed_scoop reply to a dip lick:\nresult=$(agent \"$TMPDIR\" \"curl,jq\" \"Fetch <url>, return field 'price' as a number.\")\n# Render a fresh dip with `result` inline.\n```\n\n`agent` is the right tool here because it has **no handoff**: ephemeral scoops don't notify the cone on completion, so a busy cone or sprinkle-owning scoop isn't pre-empted by every dip click. See `/workspace/skills/delegation/SKILL.md` for the full `agent` reference.\n\n## Design rules\n\n- Use S2 CSS variables for all colors (`var(--s2-content-default)`, `var(--s2-bg-layer-2)`, ...).\n- Round all displayed numbers: `Math.round()`, `.toFixed(2)`, `.toLocaleString()`.\n- Set `step` on range sliders for clean values.\n- Show errors inline (not `alert()`).\n- Sentence case always — never Title Case or ALL CAPS.\n- One root `render()` / `calc()` function triggered by all inputs.\n- Call `render()` on load with defaults so the widget is immediately interactive.\n\n## Don't\n\n- Don't hardcode hex colors or theme-specific values — use S2 tokens or `light-dark()` (see \"Light & dark mode\").\n- Don't use `@media (prefers-color-scheme: ...)` to swap colors — the parent's theme toggle is class-based and the media query desyncs.\n- Don't set `height`, `max-height`, `min-height`, `overflow: auto`, or `overflow: scroll` on dip containers — the iframe auto-sizes to content; fixed heights and internal scrollers clip it. Canvases are the one exception (they need pixel dimensions for the drawing buffer). Long content belongs in a sprinkle.\n- Don't build custom progress bars — use `.sprinkle-progress-bar`.\n- Don't build custom status dots — use `.sprinkle-status-light`.\n- Don't use numbered headings (1. File Operations) inside cards — the `__header` IS the heading.\n- Don't put prose / markdown headings between cards — let cards stand alone.\n\n## Lick patterns\n\n```javascript\n// Explicit \"send to agent\" button.\nslicc.lick({ action: 'use-config', data: { config: getCurrentConfig() } });\n\n// Lick on threshold crossing.\nif (total > budget) {\n slicc.lick({ action: 'over-budget', data: { total, budget, breakdown } });\n}\n\n// Lick on completion.\nslicc.lick({ action: 'sort-complete', data: { algorithm: algo, comparisons: n } });\n```\n\n> **Payload shape:** `slicc.lick(eventOrAction)` accepts either a plain\n> action string (`slicc.lick('cancel')`) or an `{ action, data? }`\n> object. **Always pack extra fields inside `data: { ... }`** — the\n> cone reads `event.data` as the payload, so top-level extras either\n> get dropped (sprinkle bridge) or folded into `data` as the whole\n> event object (dip bridge), neither of which gives the cone a\n> predictable shape. The canonical form works identically in both.\n\nThe agent receives the lick as a structured message and can respond with prose, another dip, or spawn a scoop.\n";export{e as default};
|
|
@@ -84,7 +84,7 @@ Never drop when:
|
|
|
84
84
|
agent <cwd> <allowed-commands> <prompt> [--model <id>] [--read-only <paths>] [--background-after <s>]
|
|
85
85
|
\`\`\`
|
|
86
86
|
|
|
87
|
-
- \`<cwd>\` — sole writable prefix (plus \`/shared/\`, the scoop's scratch folder, and \`/tmp/\`). Relative paths resolve against the caller's cwd.
|
|
87
|
+
- \`<cwd>\` — sole writable prefix (plus \`/shared/\`, the scoop's scratch folder, and \`/tmp/\` — the whole shared scratch tree, so a scoop can always reach its \`$TMPDIR\`). Relative paths resolve against the caller's cwd.
|
|
88
88
|
- \`<allowed-commands>\` — comma-separated allow-list; \`*\` for unrestricted.
|
|
89
89
|
- \`<prompt>\` — forwarded verbatim. The spawned scoop has no access to the caller's history; pack context into the prompt.
|
|
90
90
|
- \`--model\` — defaults to the parent scoop's model (or the cone's, when invoked from the terminal). Accepts an exact id, a shorthand (\`haiku\`, \`sonnet\`, \`claude-haiku-4-5\`), or the \`provider:model\` form \`models\` prints (\`openrouter:openai/gpt-5.6-terra-pro\`). A bare id resolves against the selected provider first, then against any other configured provider that offers it; if several do, the error lists the qualified ids to pick from. The scoop runs on the provider the model resolved from. An id that cannot be resolved is a hard error (exit 1) — it never silently falls back to the parent's model. Models from a provider other than the selected one must additionally be allowed in \`/etc/models\` (see [Model access policy](#model-access-policy)); a blocked model exits 1 and the error quotes the line to add.
|
|
@@ -96,7 +96,7 @@ agent <cwd> <allowed-commands> <prompt> [--model <id>] [--read-only <paths>] [--
|
|
|
96
96
|
\`\`\`bash
|
|
97
97
|
# Parallel, collected to one file. No cone turns spawned.
|
|
98
98
|
for url in site-a site-b site-c; do
|
|
99
|
-
agent
|
|
99
|
+
agent "$TMPDIR" "curl,jq" "Fetch https://$url/api, return the top-level title field." >> "$TMPDIR/titles.txt" &
|
|
100
100
|
done
|
|
101
101
|
wait
|
|
102
102
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e="---\nname: image-processing\ndescription: |\n Use this when converting, resizing, cropping, stitching, or annotating images;\n building a filmstrip, contact sheet, or grid; correcting orientation or color;\n turning a PDF into PNG or JPEG images; reading a PDF's text layer; or applying\n common ImageMagick-style effects with SLICC's `convert` / `magick`,\n `pdftoppm`, and `pdftotext` shell commands.\nallowed-tools: bash\n---\n\n# Image processing with `convert`\n\nSLICC's local ImageMagick WASM command handles common image transforms, multi-image composition, and labels without network access. `magick` is an alias for `convert`.\n\n## Compose filmstrips and grids\n\nJoin images horizontally with `+append` or vertically with `-append`:\n\n```bash\nconvert f00.jpg f01.jpg f02.jpg f03.jpg +append \"$TMPDIR/filmstrip.jpg\"\nconvert top.png middle.png bottom.png -append \"$TMPDIR/column.png\"\n```\n\nBuild grids with escaped parenthesized groups. Each group produces one intermediate image:\n\n```bash\nconvert \\( f00.jpg f01.jpg f02.jpg f03.jpg +append \\) \\\n \\( f04.jpg f05.jpg f06.jpg f07.jpg +append \\) \\\n -append \"$TMPDIR/grid.jpg\"\n```\n\n## Transform images\n\nOperations run from left to right on the preceding image or group:\n\n```bash\nconvert photo.jpg -auto-orient -thumbnail '320x320>' -strip thumb.jpg\nconvert input.png -gravity center -crop 800x600+0+0 output.png\nconvert input.png -background white -gravity center -extent 1200x630 output.png\nconvert input.png -colorspace Gray -normalize -sharpen 0x1 output.png\n```\n\nCommon operations:\n\n- Geometry: `-resize`, `-thumbnail`, `-crop`, `-extent`, `-rotate`\n- Orientation: `-auto-orient`, `-flip`, `-flop`\n- Cleanup: `-strip`, `-trim`, `-auto-gamma`, `-auto-level`, `-normalize`\n- Color/alpha: `-background`, `-alpha`, `-colorspace`, `-transparent`, `-negate`\n- Effects: `-blur R[xS]`, `-sharpen R[xS]`\n- Output: `-quality 0-100`\n\nResize geometry accepts ImageMagick modifiers such as `%`, `!`, `^`, `>`, and `<`. Quote geometry containing shell metacharacters.\n\n## Add labels\n\nConfigure the drawing state before `-annotate`:\n\n```bash\nconvert frame.jpg -gravity south -fill white -undercolor '#000000aa' \\\n -pointsize 24 -annotate +0+10 'Frame 07 — 00:03.5' labeled.jpg\n```\n\nAnnotation uses the bundled Adobe Clean font; custom `-font` selection is not supported yet.\n\nUse `-gravity` with `northwest`, `north`, `northeast`, `west`, `center`, `east`, `southwest`, `south`, or `southeast`. Quote label text and colors containing `#`.\n\n## Rasterize a PDF\n\n`pdftoppm` turns PDF pages into images. `pdftocairo` is an alias.\n\n```bash\npdftoppm -png -r 150 doc.pdf page # page-1.png, page-2.png, ...\npdftoppm -jpeg -jpegopt quality=80 doc.pdf page\npdftoppm -png -f 2 -l 4 doc.pdf page # pages 2-4 only\npdftoppm -png -singlefile doc.pdf cover # exactly cover.png, no page suffix\npdftoppm -png -scale-to 1024 doc.pdf thumb\n```\n\nPage files are numbered `<prefix>-<n>.<ext>`, zero-padded to the digit width of\nthe last page — `page-1.png` for a 9-page document, `page-01.png` for a 12-page\none. `pdftoppm` prints the filenames it wrote.\n\nFor a single page you can also feed the PDF straight to `convert`, which\nrasterizes it first and accepts ImageMagick's 0-based bracket page selector:\n\n```bash\nconvert -density 150 doc.pdf cover.png # first page\nconvert doc.pdf[2] -resize 800x page3.jpg # third page, resized\n```\n\nPrefer `pdftoppm` for whole documents: it parses the PDF once, while repeated\n`convert` calls re-parse it per page.\n\n## Read a PDF's text\n\nWhen you need the words rather than the picture, skip rasterizing: `pdftotext`\nreads the text layer directly, with no image and no vision tokens.\n\n```bash\npdftotext report.pdf - # text to stdout\npdftotext -layout invoice.pdf - # keep table columns aligned\npdftotext -f 2 -l 5 book.pdf part.txt\n```\n\nNear-empty output means a scanned PDF with no text layer: rasterize with\n`pdftoppm -png` and read the pages as images instead. There is no OCR.\n\n`pdftk` handles the rest of the page-level work (merge, split, rotate, burst)\nwithout rendering, and `pdftk in.pdf output plain.pdf uncompress` inflates the\nstreams when you need to grep the raw page operators.\n\n## Verify and inspect\n\n```bash\nfile \"$TMPDIR/grid.jpg\"\nopen --view --size high \"$TMPDIR/grid.jpg\"\nconvert --help\npdftoppm --help\npdftotext --help\n```\n\nKeep the output path last. Multiple inputs must be reduced with `+append` or `-append`; escape group parentheses as `\\(` and `\\)` so the shell passes them to `convert`.\n";export{e as default};
|
|
@@ -38,8 +38,8 @@ v86 state [-n name] save|load <file> # snapshot / restore full VM state
|
|
|
38
38
|
The fastest full-Linux guest is the pre-booted Arch state image from copy.sh (the same one behind https://copy.sh/v86/?profile=archlinux). It resumes straight into a root shell — no BIOS blobs, no boot wait:
|
|
39
39
|
|
|
40
40
|
\`\`\`bash
|
|
41
|
-
curl -o /
|
|
42
|
-
v86 start -n arch -state /
|
|
41
|
+
curl -o "$TMPDIR/arch_state.bin.zst" https://i.copy.sh/arch_state-v3.bin.zst # ~15 MB
|
|
42
|
+
v86 start -n arch -state "$TMPDIR/arch_state.bin.zst" -fs9p https://i.copy.sh/arch/ -net virtio -m 512
|
|
43
43
|
v86 text -n arch # should show a root@localhost prompt
|
|
44
44
|
v86 type -n arch "uname -a\\n"
|
|
45
45
|
v86 text -n arch
|
|
@@ -78,11 +78,11 @@ Prefer \`v86 text\` over \`screenshot\` whenever the guest is in text mode — i
|
|
|
78
78
|
|
|
79
79
|
## Live screen streaming (iframe-able)
|
|
80
80
|
|
|
81
|
-
\`v86 serve\` pumps the screen into
|
|
81
|
+
\`v86 serve\` pumps the screen into \`$TMPDIR/v86-serve-<name>/\` — a self-refreshing \`index.html\` viewer plus live \`frame.png\`/\`screen.txt\` + \`state.json\`. Mint an iframe-able preview URL from it with the regular \`serve\` command so a human (or sprinkle) can watch the VM:
|
|
82
82
|
|
|
83
83
|
\`\`\`bash
|
|
84
84
|
v86 serve -n arch --fps 4 # start the pump (1-10 fps, default 2)
|
|
85
|
-
serve /
|
|
85
|
+
serve "$TMPDIR/v86-serve-arch" # mint a worker-hosted URL to iframe
|
|
86
86
|
v86 serve -n arch --stop # stop the pump (directory stays)
|
|
87
87
|
\`\`\`
|
|
88
88
|
|
|
@@ -1 +1 @@
|
|
|
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### Updating a sprinkle (when you receive follow-up instructions)\n\n1. Edit `/shared/sprinkles/<name>/<name>.shtml` with the requested changes.\n2. Reload: `sprinkle close <name> && sprinkle open <name>`.\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 close giro-winners && sprinkle open 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 /tmp "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? }`. 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` and `data` 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. Note: the screenshot captures a DOM clone using SVG foreignObject. External stylesheets and some computed styles may not be fully reproduced. 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 / USB — parity list/request/open/close; streaming I/O stays on the realm API.\nslicc.serial.list() / request(filters?) / open(handle, options) / close(handle)\nslicc.usb.list() / request(filters?) / open(handle) / close(handle)\n```\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
|
+
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### Updating a sprinkle (when you receive follow-up instructions)\n\n1. Edit `/shared/sprinkles/<name>/<name>.shtml` with the requested changes.\n2. Reload: `sprinkle close <name> && sprinkle open <name>`.\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 close giro-winners && sprinkle open 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? }`. 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` and `data` 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. Note: the screenshot captures a DOM clone using SVG foreignObject. External stylesheets and some computed styles may not be fully reproduced. 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 / USB — parity list/request/open/close; streaming I/O stays on the realm API.\nslicc.serial.list() / request(filters?) / open(handle, options) / close(handle)\nslicc.usb.list() / request(filters?) / open(handle) / close(handle)\n```\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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e="---\nname: playwright-cli\ndescription: |\n Use this whenever the user asks to browse, navigate, click, fill a form,\n scrape, take a screenshot, or otherwise interact with a web page. SLICC drives\n the browser through the `playwright-cli` shell command (also aliased as\n `playwright` and `puppeteer`). Read this BEFORE running any browser\n automation: every tab-operating command requires a `--tab` target id, and\n multi-agent tab handling has rules you must follow.\nallowed-tools: bash\n---\n\n# Browser Automation via playwright-cli\n\nUse `playwright-cli` (also aliased as `playwright` and `puppeteer`) via the bash tool for all browser automation.\n\n**Every tab-operating command requires `--tab=<targetId>`.** There is no implicit \"current tab\". Always specify which tab you're operating on.\n\n`playwright-cli <command> --help` prints that command's usage and does nothing else — safe to run before any action.\n\n## Quick Start\n\n```bash\n# 1. Open a page — note the targetId in the output\nplaywright-cli open https://example.com\n# Output: Opened https://example.com in new tab [targetId: E9A3F...]\n\n# 2. Take a snapshot to see the page structure and get element refs\nplaywright-cli snapshot --tab=E9A3F\n\n# 3. Interact using refs from the snapshot (e.g. e5, e12)\nplaywright-cli click --tab=E9A3F e5\nplaywright-cli fill --tab=E9A3F e12 \"hello world\"\n\n# 4. Re-snapshot after interactions (refs change)\nplaywright-cli snapshot --tab=E9A3F\n```\n\n## Tab IDs\n\n- `tab-list` shows all tabs with their targetIds. The user's active tab is marked `(active)`.\n- `tab-new` / `open` return the new tab's targetId — capture it for subsequent commands.\n- Use `--tab=<targetId>` on ALL commands that operate on a tab.\n\n## Frame IDs\n\n- `frames --tab=<targetId>` lists frame IDs. A frame ID is not a tab target ID; never pass it to `--tab`.\n- Use `--tab=<targetId> --frame=<frameId>` with `eval`, `eval-file`, or `snapshot` to target a child frame, including cross-origin frames.\n- A frame-scoped `snapshot` prints only that frame's accessibility subtree. Its frame-prefixed refs work with `click`, `fill`, `dblclick`, `hover`, `select`, `check`, and `uncheck`; do not pass `--frame` to those commands. Other ref commands require refs from a top-level snapshot.\n\n## Common Failure Modes\n\n- `--tab <targetId> is required` — you forgot `--tab=<id>`. Run `tab-list` to get IDs.\n- `is a frame ID, not a tab target ID` — keep the owning tab's targetId in `--tab` and pass the frame ID with `--frame`.\n- `No snapshot available` — run `snapshot --tab=<id>` before using refs.\n- `unknown flag \"--x\"` / `unexpected argument \"y\"` — every subcommand rejects arguments it does not\n support, so an exit code of 0 means everything you passed was honoured. Check `<verb> --help`.\n- For free-text payloads that start with `-` (e.g. `fill`/`type`/`eval`), put `--` before the text:\n `fill --tab=<id> e1 -- --dash-looking`.\n- `is not an element ref` — a screenshot's positional is a main-frame element ref (`e5`); the output\n path is `--filename=<path>`. Frame-prefixed refs (`f1e5`) clip only with `click`-family commands.\n- Refs are tied to **one tab + one snapshot**. They do not carry across tabs, navigations, or reloads.\n\n## Element Refs\n\nSnapshots assign short ref IDs (`e1`, `e2`, ...) to interactive elements. Use these refs with `click`, `fill`, `dblclick`, `hover`, `select`, `check`, `uncheck`, `drag`, `upload`, `drop`, and `screenshot`.\n\nRefs are invalidated after any state-changing command. Always re-snapshot to get fresh refs.\n\n## Commands\n\nAll commands below that operate on a tab require `--tab=<targetId>`.\n\n### Core\n\n```bash\nplaywright-cli open [url] [--foreground] # Open tab (background by default), returns targetId\nplaywright-cli tab-new [url] [--foreground] # Same as open\nplaywright-cli tab-close --tab=<id> # Close tab\nplaywright-cli goto --tab=<id> <url> # Navigate tab\nplaywright-cli navigate --tab=<id> <url> # Alias for goto\nplaywright-cli snapshot --tab=<id> [--frame=<frameId>] [--no-iframes] [--filename=path] # Tab tree or one frame subtree\nplaywright-cli eval --tab=<id> [--frame=<frameId>] <expression> [--filename=path] # Evaluate JS in a tab/frame, incl. top-level await/return; use `--` before an expression that starts with `-`\nplaywright-cli eval-file --tab=<id> [--frame=<frameId>] <vfs-path> # Evaluate JS from a VFS file in a tab/frame (top-level await/return supported)\nplaywright-cli frames --tab=<id> # List frame IDs for --frame (not --tab)\nplaywright-cli resize --tab=<id> <width> <height> # Resize viewport\n```\n\n`--foreground` (or `--fg`) opens the new tab **in the foreground** — it brings the\nnew tab to the front and switches the user's visible/active tab to it, instead of\nopening in the background.\n\n**Bringing a tab to the foreground (activate / focus / raise / switch to a tab):**\n\n- **New tab** → `open <url> --foreground` (or `tab-new <url> --fg`).\n- **Existing tab** → `tab-select <index>` (1-based index from `tab-list`).\n\nThese two are the ONLY ways to change which tab is in front. There is no\n`activate`, `bring-to-front`, or `focus` subcommand, and `eval`-ing\n`window.focus()` does NOT work (browsers block a page from stealing foreground).\nIf a tab didn't come to the front, re-run `tab-select` / `--foreground` — do not\nfall back to `window.focus()`.\n\n### Interaction\n\n```bash\nplaywright-cli click --tab=<id> <ref> [--modifiers=Shift,Control,Alt,Meta] # Click element\nplaywright-cli dblclick --tab=<id> <ref> [button] [--modifiers=...] # Double-click\nplaywright-cli fill --tab=<id> <ref> <text> [--submit] # Clear input + type text (--submit presses Enter); use `--` before text that starts with `-`\nplaywright-cli type --tab=<id> <text> [--submit] # Type into focused element (--submit presses Enter); use `--` before text that starts with `-`\nplaywright-cli hover --tab=<id> <ref> # Hover over element\nplaywright-cli select --tab=<id> <ref> <value> # Select dropdown value\nplaywright-cli check --tab=<id> <ref> # Check checkbox/radio\nplaywright-cli uncheck --tab=<id> <ref> # Uncheck checkbox/radio\nplaywright-cli drag --tab=<id> <startRef> <endRef> # Drag and drop\nplaywright-cli upload --tab=<id> [ref] <file> [file...] # Upload VFS files to file input (optional ref targets hidden inputs)\nplaywright-cli drop --tab=<id> <ref> [--path=<vfs-path>] [--data=<mime/type=value>] # Drop files/data onto element\nplaywright-cli dialog-accept --tab=<id> [text] # Accept JS dialog\nplaywright-cli dialog-dismiss --tab=<id> # Dismiss JS dialog\n```\n\n### Keyboard\n\n```bash\nplaywright-cli press --tab=<id> <key> # Press key (keyDown + keyUp, e.g. Enter, Tab, Escape)\nplaywright-cli keydown --tab=<id> <key> # Hold key down (no paired keyUp)\nplaywright-cli keyup --tab=<id> <key> # Release held key (no paired keyDown)\n```\n\n### Mouse\n\n```bash\nplaywright-cli mousemove --tab=<id> <x> <y> # Move mouse to coordinates (negative coords ok, e.g. -10 20)\nplaywright-cli mousedown --tab=<id> [button] # Press mouse button (left/right/middle, default: left)\nplaywright-cli mouseup --tab=<id> [button] # Release mouse button\nplaywright-cli mousewheel --tab=<id> <dx> <dy> # Scroll mouse wheel (negative deltas ok, e.g. 0 -300)\n```\n\n### Navigation\n\n```bash\nplaywright-cli go-back --tab=<id> # history.back()\nplaywright-cli go-forward --tab=<id> # history.forward()\nplaywright-cli reload --tab=<id> # Reload page\n```\n\n### Teleport\n\n```bash\nplaywright-cli teleport --tab=<id> --start=<regex> --return=<regex> [--timeout=<s>]\nplaywright-cli teleport --list # List available follower runtimes\nplaywright-cli teleport --off --tab=<id> # Cancel a teleport on this tab\nplaywright-cli open <url> --teleport-start=<regex> --teleport-return=<regex>\nplaywright-cli goto --tab=<id> <url> --teleport-start=<regex> --teleport-return=<regex>\n```\n\nTeleport is for leader/follower tray auth handoffs. Scoped to a specific tab — only commands targeting the teleporting tab are blocked; other tabs remain operational.\n\n`--runtime` is optional: without it, teleport picks the most recently active eligible follower (preferring standalone floats). Only followers that can actually serve cookies are eligible — cherry hosts, exec-only CLI followers, and any target advertising `network: false` are excluded, so a teleport fails fast instead of \"succeeding\" with zero cookies.\n\n### Screenshots\n\n```bash\nplaywright-cli screenshot --tab=<id> # Save to $TMPDIR/screenshot-<ts>.png\nplaywright-cli screenshot --tab=<id> --filename=page.png # Save to custom path\nplaywright-cli screenshot --tab=<id> e5 # Clip to an element (positional = MAIN-FRAME ref, not a path)\nplaywright-cli screenshot --tab=<id> --fullPage # Full scrollable page (alias: --full-page)\nplaywright-cli screenshot --tab=<id> --max-width=800 # Downscale to a max width\n```\n\nAn element screenshot (`screenshot e5`) returns **that element's crop or fails**\n(exit 1) — typically because the snapshot went stale after a navigation or\nlayout change. Re-run `snapshot` and retry with a fresh ref; it never silently\nsubstitutes a full-viewport frame.\n\nA `resize` sticks to its tab: the viewport override is re-applied automatically\nwhenever the tab is re-attached, so another driver switching tabs cannot reset\nit.\n\nAll playwright-cli commands share one browser and run **serialized**. When\nconcurrent callers (e.g. parallel scoops) queue up, commands may emit a\n`note: browser bridge contended — ...` line on stderr with the total lock wait\nand queue depth — treat it as a signal to stagger callers or reduce fan-out\nrather than re-running commands.\n\n### Save As\n\n```bash\nplaywright-cli pdf --tab=<id> [--filename=path] # Save page as PDF (not available in extension mode)\n```\n\n### Viewing pages and screenshots yourself\n\nThe browser displays things to the human; `open --view` is what lets _you_ see them. **But viewing screenshots is a last resort for the cone — every image you load eats a large chunk of the context window** (a single 1280×800 PNG can run 1500+ tokens, full-page screenshots much more). Reach for cheaper signals first:\n\n1. **`playwright-cli snapshot --tab=<id>`** — text accessibility tree. Use this first; it answers \"what's on the page\" for almost all verification tasks at a tiny fraction of the token cost.\n2. **`eval` against the DOM** — when you need a specific value (`document.title`, an attribute, computed style), `eval` it. Don't screenshot for facts you can extract.\n3. **Delegate visual inspection to a scoop.** If the cone genuinely needs vision (layout regression, render fidelity, \"does this look right\"), spawn a scoop to take and view the screenshot — the scoop's context absorbs the tokens, and you receive its summary back. The cone's window stays clean for orchestration.\n4. **`open --view` in the cone** — only when the cone itself must see pixels for its current decision and steps 1–3 won't do.\n\n**What you CAN see:**\n\n- `open --view <path>` — reads an image from the VFS and returns it. Works with PNG, JPEG, GIF, WebP, SVG.\n- `playwright-cli screenshot --tab=<id>` + `open --view <path>` — screenshot a tab, then view it.\n- `screencapture --view screenshot.png` — capture the user's screen via browser screen sharing.\n- `playwright-cli snapshot --tab=<id>` — accessibility tree (text). Use to verify content without vision.\n\n**What only the human sees:**\n\n- `serve <dir>` — opens an app directory in a browser tab (read-only preview).\n- `serve --ttl 30d <dir>` — uploads an immutable snapshot that works without the leader. TTL units are whole `m`, `h`, `d`, or `w`, capped at 30 days. It implies `--no-bridge`, conflicts with `--bridge`/`--max-tabs`, and is limited to 1,000 files, 25 MiB per file, and 50 MiB total. Use `serve --list` for mode/expiry and `serve --stop <token>` to revoke it early.\n- `serve --bridge <dir>` — opens a **driveable** preview whose visitors auto-connect as live synthetic-CDP targets you can navigate/click/evaluate/screenshot via playwright. **Security: opt-in only; cross-subdomain cookie risk accepted (host-only cookies isolated; `Domain=.sliccy.now` cookies readable across previews).** Flags: `--max-tabs <N>` (default 20), `--quiet` (suppress the first-visit announcement), `--no-bridge` (force read-only), `--stop <token>` (revoke + delete webhook). Use `serve --logs [<token>] [--lines <N>]` to inspect leader-memory-only connect/disconnect records without emitting a lick or waking the cone. `serve --truncate [<token>]` clears matching records **and re-arms the first-visit announcement**, so the next visit announces once. Visitor page API: `window.slicc.emit(name, detail?)` (fires webhook lick on cone), `window.slicc.on(name, cb)` (subscribes to CustomEvents you dispatch).\n- `open <path>` (no flags) — opens a file in a browser tab.\n- `imgcat <path>` — displays an image in the terminal preview.\n\n**Workflow to verify a page (when vision is actually required):**\n\n1. `serve /workspace/app` — open the app (the human sees it).\n2. `playwright-cli tab-list` — find the tab by URL, note the targetId.\n3. `playwright-cli snapshot --tab=<id>` — required before screenshot, and often answers your question on its own.\n4. `playwright-cli screenshot --tab=<id> --filename=\"$TMPDIR/shot.png\"` — consider `--max-width` to keep the file small.\n5. `open --view \"$TMPDIR/shot.png\"` — now you can see it. Strongly prefer doing this from a scoop, not the cone.\n\n**Don't:**\n\n- Default to screenshots when a snapshot would do.\n- `read_file` on a PNG or base64-encode to view images.\n- `imgcat` or `cat` on screenshots expecting to see them.\n- Open a screenshot then screenshot that tab.\n- Use `eval` to check the active tab — use `tab-list`.\n\n### Tab Management\n\n```bash\nplaywright-cli tab-list # List tabs with targetIds + (active) marker\nplaywright-cli tab-new [url] # New tab, returns targetId\nplaywright-cli tab-close --tab=<id> # Close specific tab\nplaywright-cli tab-select <index> # Select (bring to front) tab by 1-based index\n```\n\n### Cookies\n\n```bash\nplaywright-cli cookie-list --tab=<id> [--domain=<d>] [--path=<p>] # List cookies (filter by domain/path)\nplaywright-cli cookie-get --tab=<id> <name> # Get cookie\nplaywright-cli cookie-set --tab=<id> <name> <value> [--sameSite=Strict|Lax|None] [other flags] # Set cookie\nplaywright-cli cookie-delete --tab=<id> <name> [--domain= --path=] # Delete cookie\nplaywright-cli cookie-clear --tab=<id> # Clear all cookies\n```\n\n### Storage\n\n```bash\nplaywright-cli localstorage-list --tab=<id>\nplaywright-cli localstorage-get --tab=<id> <key>\nplaywright-cli localstorage-set --tab=<id> <key> <value>\nplaywright-cli localstorage-delete --tab=<id> <key>\nplaywright-cli localstorage-clear --tab=<id>\n# Same pattern for sessionstorage-*\nplaywright-cli state-save --tab=<id> [filename|--filename=path] # Save cookies + localStorage to JSON\nplaywright-cli state-load --tab=<id> <filename> # Restore from state file\n```\n\n### Network\n\n```bash\nplaywright-cli console --tab=<id> [min-level] [--clear] # List console messages (debug/log/info/warning/error)\nplaywright-cli requests --tab=<id> [--static] [--filter=<regex>] [--clear] # List network requests\nplaywright-cli request --tab=<id> <index> [--filename=path] # Full request details\nplaywright-cli request-headers --tab=<id> <index> # Request headers only\nplaywright-cli request-body --tab=<id> <index> # Request body only\nplaywright-cli response-headers --tab=<id> <index> # Response headers only\nplaywright-cli response-body --tab=<id> <index> [--filename=path] # Response body (saves binary to file)\nplaywright-cli network-state-set --tab=<id> <online|offline> # Toggle network state\nplaywright-cli route --tab=<id> <pattern> [--status=N] [--body=text] [--content-type=type] [--header=name:value]\nplaywright-cli route-list --tab=<id> # List active routes\nplaywright-cli unroute --tab=<id> [pattern] # Remove routes (all if no pattern)\n```\n\n### DevTools\n\n```bash\nplaywright-cli generate-locator --tab=<id> <ref> # Generate Playwright locator string for element\nplaywright-cli highlight --tab=<id> <ref> [--style=<css>] # Highlight element with visual overlay\nplaywright-cli highlight --tab=<id> --hide [ref] # Remove highlight (all if no ref)\n```\n\n### HAR Recording\n\n```bash\nplaywright-cli record [url] [--filter=<js-expr>] # Open tab with network recording\nplaywright-cli stop-recording <recordingId> # Stop and save HAR\n```\n\n## Multi-Agent Tab Behavior\n\n**All agents (cone + scoops) share the same tab namespace.** There is no tab isolation.\n\n- `tab-list` shows **every** tab from every agent — yours, the cone's, other scoops'. The list can be noisy.\n- Any agent can `eval`, `snapshot`, or `close` any tab — there are no ownership checks.\n- Tab counts fluctuate as other agents open and close tabs concurrently.\n\n**Best practices for scoops:**\n\n1. **Track your own tab IDs.** When you open a tab, capture the targetId and store it. Don't rely on `tab-list` to find your tabs later — other agents' tabs will be mixed in.\n\n ```bash\n # Open and capture the ID\n playwright-cli tab-new https://example.com\n # Output: Opened https://example.com in new tab [targetId: ABC123...]\n # Use ABC123 for all subsequent commands on this tab\n ```\n\n2. **NEVER close tabs you didn't open.** Tabs you don't recognize belong to the **user** or other agents. User tabs are off-limits unless the user explicitly asks you to close them. Only close tabs whose targetId you captured from your own `tab-new` / `open` calls.\n\n3. **Handle \"tab not found\" gracefully.** Another agent might close a tab between your `tab-list` and your command. If you get `Error: No tab with id`, the tab is gone — move on.\n\n4. **Don't depend on tab count or ordering.** Other agents are opening/closing tabs concurrently. Use targetIds, not positional logic.\n\n5. **Clean up when done.** Close all tabs you opened before finishing. Include this in every scoop brief:\n _\"Close each tab with `playwright-cli tab-close --tab=<id>` when done.\"_\n\n## Tips\n\n- **Refs change after every interaction** — always re-snapshot before clicking or filling.\n- `open` and `tab-new` open tabs in the **background** by default. Capture the targetId from the output. To open in the **foreground** add `--foreground`/`--fg`; to raise an **already-open** tab use `tab-select <index>`.\n- After `click`, `fill`, `goto`, `go-back`, `go-forward`, `reload`, `select`, `check`, `uncheck`, `drag`, or `dialog-*`, take a fresh `snapshot --tab=<id>` before using refs again.\n- Unexpected JavaScript dialogs are auto-dismissed on attached pages.\n- Use `eval --tab=<id>` for DOM operations not covered by built-in commands; save results with `--filename=path`.\n- The SLICC app tab and Chrome internal UI tabs are automatically excluded from `tab-list`.\n- `fill` clears and types into regular inputs, textareas, and `contenteditable` elements. Use `--submit` to press Enter after. If the text or an `eval` expression starts with `-`, put `--` before it so it is not parsed as a flag.\n- Negative numbers for `mousewheel` / `mousemove` are positionals (no `--` needed): `mousewheel --tab=<id> 0 -300`.\n- Screenshots default to `$TMPDIR/screenshot-<timestamp>.png` — your own scratch directory, not the shared root. Use `--filename=path` to save elsewhere —\n `screenshot <path>` is an error, because that slot is the element ref.\n- Unsupported flags and extra positionals are rejected per subcommand, so a probe that exits 0 means\n the flag was honoured — nothing is silently dropped.\n- Use `keydown`/`keyup` for holding modifier keys (e.g. Shift+click: `keydown Shift`, `click`, `keyup Shift`).\n- Use `requests` + `response-body` to inspect XHR/fetch responses; `route` to mock or block them.\n- **Calling the app's own backend? Use `curlwright`, not `eval-file`.** It is curl's flags run by a `fetch()` inside the tab, so cookies, origin and session come along: `curlwright -s -X POST https://app.example.com/api/items -H 'X-CSRF-Token: abc' -d '{\"name\":\"x\"}' --tab=<id>`. `-o <file>` writes a **byte-exact** body, which `eval-file` cannot do at all — that is the only way to pull a binary response out of a page. Without `--tab` it uses the tab already on that origin. `curlwright --help` for the flag list.\n- `state-save` / `state-load` persist auth state (cookies + localStorage) across sessions.\n- `pdf` saves a print-layout PDF; not available in extension mode.\n\n## Low-level CDP escape hatch\n\nFor raw Chrome DevTools Protocol calls that `playwright-cli` doesn't wrap, send JSON-RPC directly over WebSocket with `websocat`:\n\n```bash\n# Discover the page's debug socket URL via the CDP HTTP endpoint\ncurl -s http://127.0.0.1:9222/json | jq -r '.[0].webSocketDebuggerUrl'\n\n# Send a single CDP method, receive the response, exit\necho 'Page.navigate {\"url\":\"https://example.com\"}' \\\n | websocat -1 --jsonrpc --jsonrpc-omit-jsonrpc ws://127.0.0.1:9222/devtools/page/<id>\n```\n\nRun `websocat --help` for the full flag list. Use this only when `playwright-cli` has no wrapper for the CDP method you need.\n";export{e as default};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/oauth-service-DJuzPXsg.js","assets/preload-helper-Czpn1I53.js","assets/proxied-fetch-CFWkfPNA.js","assets/rolldown-runtime-Dd_uD5pT.js","assets/base64-DJRlyqZu.js","assets/src-CAWTvdAa.js","assets/runtime-env-Dlaokqg1.js","assets/fetch-body-D2PwksUW.js","assets/api-endpoint-GRCyib6X.js","assets/runtime-env-CgsZ7LTp.js","assets/runtime-env-E-i2SW1s.js","assets/panel-rpc-B3MSVImO.js","assets/permissions-surface-registry-CilCRwhh.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{n as e,t}from"./rolldown-runtime-Dd_uD5pT.js";import{t as n}from"./preload-helper-Czpn1I53.js";import{a as r,c as i,l as a,o,s}from"./claude-model-version-C0HLsjny.js";import"./libesm-BypnjKEL.js";import{t as c}from"./logger-DDBAeTLF.js";import"./src-CAWTvdAa.js";import"./proxied-fetch-CFWkfPNA.js";import{i as l,t as u}from"./api-endpoint-GRCyib6X.js";import{n as d}from"./float-topology-BYb7rxY5.js";import"./secret-topology-BE7Eyd3P.js";import{t as f}from"./secrets-bridge-client-CHZomqvU.js";import"./image-markers-CxJ9ZBgw.js";import"./tool-ui-DfXu6quJ.js";import{n as p,o as m,t as h}from"./providers-DvmKM7--.js";import{n as g,t as _}from"./panel-rpc-B3MSVImO.js";import{r as v,t as y}from"./bedrock-camp-compat-CgBMwna8.js";var b={assign:0,create:0,clone:0,discard:0,update:0};function x(e){return Array.isArray(e)}function S(e){return w(e,null)}function C(e){return w(typeof e,`object`)&&!S(e)}function w(e,t){return e===t}function ee(e){if(!C(e))return!1;let t=globalThis.Object.getPrototypeOf(e);return!S(t)&&w(typeof t.constructor,`function`)&&!(w(t.constructor,globalThis.Object)||w(t.constructor.name,`Object`))}function te(e){return w(e,`__proto__`)||w(e,`constructor`)||w(e,`prototype`)}function ne(e,t){return te(t)?Object.prototype.hasOwnProperty.call(e,t):t in e}function re(e){return Object.getOwnPropertyNames(e)}function ie(e){return Object.getOwnPropertySymbols(e)}function T(e){return globalThis.ArrayBuffer.isView(e)}function ae(e){return e instanceof globalThis.RegExp}function oe(e){return e instanceof globalThis.Set}function se(e){return e instanceof globalThis.Map}function ce(e){return e}function le(e){return ne(e,`~kind`)||ne(e,`~unsafe`)}function ue(e){let t={},n=Object.getOwnPropertyDescriptors(e);for(let e of Object.keys(n)){if(te(e))continue;let r=n[e];ne(r,`value`)&&Object.defineProperty(t,e,{...r,value:E(r.value)})}return t}function de(e){let t={};for(let n of re(e))te(n)||(t[n]=E(e[n]));for(let n of ie(e))t[n]=E(e[n]);return t}function fe(e){return ee(e)?ce(e):le(e)?ue(e):de(e)}function pe(e){return e.map(e=>E(e))}function me(e){return e.slice()}function he(e){return new RegExp(e.source,e.flags)}function ge(e){return new Map(E([...e.entries()]))}function _e(e){return new Set(E([...e.values()]))}function E(e){return T(e)?me(e):ae(e)?he(e):se(e)?ge(e):oe(e)?_e(e):x(e)?pe(e):C(e)?fe(e):e}function ve(e){return b.clone+=1,E(e)}var ye={immutableTypes:!1,maxErrors:8,useAcceleration:!0,exactOptionalPropertyTypes:!1,enumerableKind:!1,correctiveParse:!1,unionPrioritySort:!0};function be(){return ye}function xe(e,t){for(let n of Object.keys(t))Object.defineProperty(e,n,{configurable:!0,writable:!0,enumerable:!1,value:t[n]});return e}function Se(e,t){return{...e,...t}}function Ce(e,t,n={}){b.create+=1;let r=be(),i=Se(t,n),a=r.enumerableKind?Se(i,e):xe(i,e);return r.immutableTypes?Object.freeze(a):a}function we(e,t,n){b.update+=1;let r=be(),i=ve(e);for(let e of Object.keys(t))Object.defineProperty(i,e,{configurable:!0,writable:!0,enumerable:r.enumerableKind,value:t[e]});for(let e of Object.keys(n))Object.defineProperty(i,e,{configurable:!0,enumerable:!0,writable:!0,value:n[e]});return i}function Te(e){return C(e)}function Ee(e){return we(e,{"~optional":!0},{})}function De(e,t){return we(Ee(e),{},t)}function Oe(e,t){return Ce({"~kind":`Array`},{type:`array`,items:e},t)}function ke(e,t={}){return De(e,t)}function Ae(e){return ke(e)}function je(e){return Te(e)&&ne(e,`~optional`)}function Me(e){return re(e).filter(t=>!je(e[t]))}function Ne(e,t={}){let n=Me(e);return Ce({"~kind":`Object`},{type:`object`,...n.length>0?{required:n}:{},properties:e},t)}var Pe;(function(e){e[e.Array=0]=`Array`,e[e.BigInt=1]=`BigInt`,e[e.Boolean=2]=`Boolean`,e[e.Date=3]=`Date`,e[e.Constructor=4]=`Constructor`,e[e.Function=5]=`Function`,e[e.Null=6]=`Null`,e[e.Number=7]=`Number`,e[e.Object=8]=`Object`,e[e.RegExp=9]=`RegExp`,e[e.String=10]=`String`,e[e.Symbol=11]=`Symbol`,e[e.TypeArray=12]=`TypeArray`,e[e.Undefined=13]=`Undefined`})(Pe||={}),Array.from({length:256}).map((e,t)=>BigInt(t));var Fe=new Float64Array(1);new DataView(Fe.buffer),new Uint8Array(Fe.buffer),new TextEncoder;var Ie=`-?(?:0|[1-9][0-9]*)`,Le=`-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?`;function Re(e){return Ce({"~kind":`Number`},{type:`number`},e)}function D(e){return Ce({"~kind":`String`},{type:`string`},e)}var ze=`^${Ie}$`;`${Le}`;function Be(e,t){return Array.from({length:t-e+1},(t,n)=>String.fromCharCode(e+n))}var Ve=[...Be(97,122),...Be(65,90)],He=[`0`,...Be(49,57)];[...He],[...Ve,...He],[...He],new RegExp(ze);function Ue(e,t){try{return Promise.resolve(t(We))}catch(e){return Promise.reject(e)}}var We={startSpan:Ue,addEvent:()=>{},setAttributes:()=>{},setStatus:()=>{}};Object.freeze(We);var Ge=Symbol.for(`yaml.alias`),Ke=Symbol.for(`yaml.document`),O=Symbol.for(`yaml.map`),qe=Symbol.for(`yaml.pair`),Je=Symbol.for(`yaml.scalar`),Ye=Symbol.for(`yaml.seq`),k=Symbol.for(`yaml.node.type`),A=e=>!!e&&typeof e==`object`&&e[k]===Ge,Xe=e=>!!e&&typeof e==`object`&&e[k]===Ke,Ze=e=>!!e&&typeof e==`object`&&e[k]===O,j=e=>!!e&&typeof e==`object`&&e[k]===qe,M=e=>!!e&&typeof e==`object`&&e[k]===Je,Qe=e=>!!e&&typeof e==`object`&&e[k]===Ye;function N(e){if(e&&typeof e==`object`)switch(e[k]){case O:case Ye:return!0}return!1}function P(e){if(e&&typeof e==`object`)switch(e[k]){case Ge:case O:case Je:case Ye:return!0}return!1}var $e=e=>(M(e)||N(e))&&!!e.anchor,F=Symbol(`break visit`),et=Symbol(`skip children`),tt=Symbol(`remove node`);function nt(e,t){let n=rt(t);Xe(e)?I(null,e.contents,n,Object.freeze([e]))===tt&&(e.contents=null):I(null,e,n,Object.freeze([]))}nt.BREAK=F,nt.SKIP=et,nt.REMOVE=tt;function I(e,t,n,r){let i=it(e,t,n,r);if(P(i)||j(i))return at(e,r,i),I(e,i,n,r);if(typeof i!=`symbol`){if(N(t)){r=Object.freeze(r.concat(t));for(let e=0;e<t.items.length;++e){let i=I(e,t.items[e],n,r);if(typeof i==`number`)e=i-1;else if(i===F)return F;else i===tt&&(t.items.splice(e,1),--e)}}else if(j(t)){r=Object.freeze(r.concat(t));let e=I(`key`,t.key,n,r);if(e===F)return F;e===tt&&(t.key=null);let i=I(`value`,t.value,n,r);if(i===F)return F;i===tt&&(t.value=null)}}return i}function rt(e){return typeof e==`object`&&(e.Collection||e.Node||e.Value)?Object.assign({Alias:e.Node,Map:e.Node,Scalar:e.Node,Seq:e.Node},e.Value&&{Map:e.Value,Scalar:e.Value,Seq:e.Value},e.Collection&&{Map:e.Collection,Seq:e.Collection},e):e}function it(e,t,n,r){if(typeof n==`function`)return n(e,t,r);if(Ze(t))return n.Map?.(e,t,r);if(Qe(t))return n.Seq?.(e,t,r);if(j(t))return n.Pair?.(e,t,r);if(M(t))return n.Scalar?.(e,t,r);if(A(t))return n.Alias?.(e,t,r)}function at(e,t,n){let r=t[t.length-1];if(N(r))r.items[e]=n;else if(j(r))e===`key`?r.key=n:r.value=n;else if(Xe(r))r.contents=n;else{let e=A(r)?`alias`:`scalar`;throw Error(`Cannot replace node with ${e} parent`)}}function ot(e){if(/[\x00-\x19\s,[\]{}]/.test(e)){let t=`Anchor must not contain whitespace or control characters: ${JSON.stringify(e)}`;throw Error(t)}return!0}function st(e,t,n,r){if(r&&typeof r==`object`){if(Array.isArray(r))for(let t=0,n=r.length;t<n;++t){let n=r[t],i=st(e,r,String(t),n);i===void 0?delete r[t]:i!==n&&(r[t]=i)}else if(r instanceof Map)for(let t of Array.from(r.keys())){let n=r.get(t),i=st(e,r,t,n);i===void 0?r.delete(t):i!==n&&r.set(t,i)}else if(r instanceof Set)for(let t of Array.from(r)){let n=st(e,r,t,t);n===void 0?r.delete(t):n!==t&&(r.delete(t),r.add(n))}else for(let[t,n]of Object.entries(r)){let i=st(e,r,t,n);i===void 0?delete r[t]:i!==n&&(r[t]=i)}}return e.call(t,n,r)}function L(e,t,n){if(Array.isArray(e))return e.map((e,t)=>L(e,String(t),n));if(e&&typeof e.toJSON==`function`){if(!n||!$e(e))return e.toJSON(t,n);let r={aliasCount:0,count:1,res:void 0};n.anchors.set(e,r),n.onCreate=e=>{r.res=e,delete n.onCreate};let i=e.toJSON(t,n);return n.onCreate&&n.onCreate(i),i}return typeof e==`bigint`&&!n?.keep?Number(e):e}var ct=class{constructor(e){Object.defineProperty(this,k,{value:e})}clone(){let e=Object.create(Object.getPrototypeOf(this),Object.getOwnPropertyDescriptors(this));return this.range&&(e.range=this.range.slice()),e}toJS(e,{mapAsMap:t,maxAliasCount:n,onAnchor:r,reviver:i}={}){if(!Xe(e))throw TypeError(`A document argument is required`);let a={anchors:new Map,doc:e,keep:!0,mapAsMap:t===!0,mapKeyWarned:!1,maxAliasCount:typeof n==`number`?n:100},o=L(this,``,a);if(typeof r==`function`)for(let{count:e,res:t}of a.anchors.values())r(t,e);return typeof i==`function`?st(i,{"":o},``,o):o}},lt=class extends ct{constructor(e){super(Ge),this.source=e,Object.defineProperty(this,"tag",{set(){throw Error(`Alias nodes cannot have tags`)}})}resolve(e,t){if(t?.maxAliasCount===0)throw ReferenceError(`Alias resolution is disabled`);let n;t?.aliasResolveCache?n=t.aliasResolveCache:(n=[],nt(e,{Node:(e,t)=>{(A(t)||$e(t))&&n.push(t)}}),t&&(t.aliasResolveCache=n));let r;for(let e of n){if(e===this)break;e.anchor===this.source&&(r=e)}return r}toJSON(e,t){if(!t)return{source:this.source};let{anchors:n,doc:r,maxAliasCount:i}=t,a=this.resolve(r,t);if(!a){let e=`Unresolved alias (the anchor must be set before the alias): ${this.source}`;throw ReferenceError(e)}let o=n.get(a);if(o||=(L(a,null,t),n.get(a)),o?.res===void 0)throw ReferenceError(`This should not happen: Alias anchor was not resolved?`);if(i>=0&&(o.count+=1,o.aliasCount===0&&(o.aliasCount=ut(r,a,n)),o.count*o.aliasCount>i))throw ReferenceError(`Excessive alias count indicates a resource exhaustion attack`);return o.res}toString(e,t,n){let r=`*${this.source}`;if(e){if(ot(this.source),e.options.verifyAliasOrder&&!e.anchors.has(this.source)){let e=`Unresolved alias (the anchor must be set before the alias): ${this.source}`;throw Error(e)}if(e.implicitKey)return`${r} `}return r}};function ut(e,t,n){if(A(t)){let r=t.resolve(e),i=n&&r&&n.get(r);return i?i.count*i.aliasCount:0}if(N(t)){let r=0;for(let i of t.items){let t=ut(e,i,n);t>r&&(r=t)}return r}if(j(t)){let r=ut(e,t.key,n),i=ut(e,t.value,n);return Math.max(r,i)}return 1}var dt=e=>!e||typeof e!=`function`&&typeof e!=`object`,R=class extends ct{constructor(e){super(Je),this.value=e}toJSON(e,t){return t?.keep?this.value:L(this.value,e,t)}toString(){return String(this.value)}};R.BLOCK_FOLDED=`BLOCK_FOLDED`,R.BLOCK_LITERAL=`BLOCK_LITERAL`,R.PLAIN=`PLAIN`,R.QUOTE_DOUBLE=`QUOTE_DOUBLE`,R.QUOTE_SINGLE=`QUOTE_SINGLE`;var ft=`tag:yaml.org,2002:`;function pt(e,t,n){if(t){let e=n.filter(e=>e.tag===t),r=e.find(e=>!e.format)??e[0];if(!r)throw Error(`Tag ${t} not found`);return r}return n.find(t=>t.identify?.(e)&&!t.format)}function mt(e,t,n){if(Xe(e)&&(e=e.contents),P(e))return e;if(j(e)){let t=n.schema[O].createNode?.(n.schema,null,n);return t.items.push(e),t}(e instanceof String||e instanceof Number||e instanceof Boolean||typeof BigInt<`u`&&e instanceof BigInt)&&(e=e.valueOf());let{aliasDuplicateObjects:r,onAnchor:i,onTagObj:a,schema:o,sourceObjects:s}=n,c;if(r&&e&&typeof e==`object`){if(c=s.get(e),c)return c.anchor??(c.anchor=i(e)),new lt(c.anchor);c={anchor:null,node:null},s.set(e,c)}t?.startsWith(`!!`)&&(t=ft+t.slice(2));let l=pt(e,t,o.tags);if(!l){if(e&&typeof e.toJSON==`function`&&(e=e.toJSON()),!e||typeof e!=`object`){let t=new R(e);return c&&(c.node=t),t}l=e instanceof Map?o[O]:Symbol.iterator in Object(e)?o[Ye]:o[O]}a&&(a(l),delete n.onTagObj);let u=l?.createNode?l.createNode(n.schema,e,n):typeof l?.nodeClass?.from==`function`?l.nodeClass.from(n.schema,e,n):new R(e);return t?u.tag=t:l.default||(u.tag=l.tag),c&&(c.node=u),u}function ht(e,t,n){let r=n;for(let e=t.length-1;e>=0;--e){let n=t[e];if(typeof n==`number`&&Number.isInteger(n)&&n>=0){let e=[];e[n]=r,r=e}else r=new Map([[n,r]])}return mt(r,void 0,{aliasDuplicateObjects:!1,keepUndefined:!1,onAnchor:()=>{throw Error(`This should not happen, please report a bug.`)},schema:e,sourceObjects:new Map})}var gt=e=>e==null||typeof e==`object`&&!!e[Symbol.iterator]().next().done,_t=class extends ct{constructor(e,t){super(e),Object.defineProperty(this,"schema",{value:t,configurable:!0,enumerable:!1,writable:!0})}clone(e){let t=Object.create(Object.getPrototypeOf(this),Object.getOwnPropertyDescriptors(this));return e&&(t.schema=e),t.items=t.items.map(t=>P(t)||j(t)?t.clone(e):t),this.range&&(t.range=this.range.slice()),t}addIn(e,t){if(gt(e))this.add(t);else{let[n,...r]=e,i=this.get(n,!0);if(N(i))i.addIn(r,t);else if(i===void 0&&this.schema)this.set(n,ht(this.schema,r,t));else throw Error(`Expected YAML collection at ${n}. Remaining path: ${r}`)}}deleteIn(e){let[t,...n]=e;if(n.length===0)return this.delete(t);let r=this.get(t,!0);if(N(r))return r.deleteIn(n);throw Error(`Expected YAML collection at ${t}. Remaining path: ${n}`)}getIn(e,t){let[n,...r]=e,i=this.get(n,!0);return r.length===0?!t&&M(i)?i.value:i:N(i)?i.getIn(r,t):void 0}hasAllNullValues(e){return this.items.every(t=>{if(!j(t))return!1;let n=t.value;return n==null||e&&M(n)&&n.value==null&&!n.commentBefore&&!n.comment&&!n.tag})}hasIn(e){let[t,...n]=e;if(n.length===0)return this.has(t);let r=this.get(t,!0);return N(r)?r.hasIn(n):!1}setIn(e,t){let[n,...r]=e;if(r.length===0)this.set(n,t);else{let e=this.get(n,!0);if(N(e))e.setIn(r,t);else if(e===void 0&&this.schema)this.set(n,ht(this.schema,r,t));else throw Error(`Expected YAML collection at ${n}. Remaining path: ${r}`)}}},vt=e=>e.replace(/^(?!$)(?: $)?/gm,`#`);function yt(e,t){return/^\n+$/.test(e)?e.substring(1):t?e.replace(/^(?! *$)/gm,t):e}var z=(e,t,n)=>e.endsWith(`
|
|
2
|
+
import{n as e,t}from"./rolldown-runtime-Dd_uD5pT.js";import{t as n}from"./preload-helper-Czpn1I53.js";import{a as r,c as i,l as a,o,s}from"./claude-model-version-C0HLsjny.js";import"./libesm-BypnjKEL.js";import{t as c}from"./logger-DDBAeTLF.js";import"./src-CAWTvdAa.js";import"./proxied-fetch-CFWkfPNA.js";import{i as l,t as u}from"./api-endpoint-GRCyib6X.js";import{n as d}from"./float-topology-BYb7rxY5.js";import"./secret-topology-BE7Eyd3P.js";import{t as f}from"./secrets-bridge-client-CHZomqvU.js";import"./image-markers-CxJ9ZBgw.js";import"./tool-ui-DfXu6quJ.js";import{n as p,o as m,t as h}from"./providers-B3XsxeEV.js";import{n as g,t as _}from"./panel-rpc-B3MSVImO.js";import{r as v,t as y}from"./bedrock-camp-compat-CgBMwna8.js";var b={assign:0,create:0,clone:0,discard:0,update:0};function x(e){return Array.isArray(e)}function S(e){return w(e,null)}function C(e){return w(typeof e,`object`)&&!S(e)}function w(e,t){return e===t}function ee(e){if(!C(e))return!1;let t=globalThis.Object.getPrototypeOf(e);return!S(t)&&w(typeof t.constructor,`function`)&&!(w(t.constructor,globalThis.Object)||w(t.constructor.name,`Object`))}function te(e){return w(e,`__proto__`)||w(e,`constructor`)||w(e,`prototype`)}function ne(e,t){return te(t)?Object.prototype.hasOwnProperty.call(e,t):t in e}function re(e){return Object.getOwnPropertyNames(e)}function ie(e){return Object.getOwnPropertySymbols(e)}function T(e){return globalThis.ArrayBuffer.isView(e)}function ae(e){return e instanceof globalThis.RegExp}function oe(e){return e instanceof globalThis.Set}function se(e){return e instanceof globalThis.Map}function ce(e){return e}function le(e){return ne(e,`~kind`)||ne(e,`~unsafe`)}function ue(e){let t={},n=Object.getOwnPropertyDescriptors(e);for(let e of Object.keys(n)){if(te(e))continue;let r=n[e];ne(r,`value`)&&Object.defineProperty(t,e,{...r,value:E(r.value)})}return t}function de(e){let t={};for(let n of re(e))te(n)||(t[n]=E(e[n]));for(let n of ie(e))t[n]=E(e[n]);return t}function fe(e){return ee(e)?ce(e):le(e)?ue(e):de(e)}function pe(e){return e.map(e=>E(e))}function me(e){return e.slice()}function he(e){return new RegExp(e.source,e.flags)}function ge(e){return new Map(E([...e.entries()]))}function _e(e){return new Set(E([...e.values()]))}function E(e){return T(e)?me(e):ae(e)?he(e):se(e)?ge(e):oe(e)?_e(e):x(e)?pe(e):C(e)?fe(e):e}function ve(e){return b.clone+=1,E(e)}var ye={immutableTypes:!1,maxErrors:8,useAcceleration:!0,exactOptionalPropertyTypes:!1,enumerableKind:!1,correctiveParse:!1,unionPrioritySort:!0};function be(){return ye}function xe(e,t){for(let n of Object.keys(t))Object.defineProperty(e,n,{configurable:!0,writable:!0,enumerable:!1,value:t[n]});return e}function Se(e,t){return{...e,...t}}function Ce(e,t,n={}){b.create+=1;let r=be(),i=Se(t,n),a=r.enumerableKind?Se(i,e):xe(i,e);return r.immutableTypes?Object.freeze(a):a}function we(e,t,n){b.update+=1;let r=be(),i=ve(e);for(let e of Object.keys(t))Object.defineProperty(i,e,{configurable:!0,writable:!0,enumerable:r.enumerableKind,value:t[e]});for(let e of Object.keys(n))Object.defineProperty(i,e,{configurable:!0,enumerable:!0,writable:!0,value:n[e]});return i}function Te(e){return C(e)}function Ee(e){return we(e,{"~optional":!0},{})}function De(e,t){return we(Ee(e),{},t)}function Oe(e,t){return Ce({"~kind":`Array`},{type:`array`,items:e},t)}function ke(e,t={}){return De(e,t)}function Ae(e){return ke(e)}function je(e){return Te(e)&&ne(e,`~optional`)}function Me(e){return re(e).filter(t=>!je(e[t]))}function Ne(e,t={}){let n=Me(e);return Ce({"~kind":`Object`},{type:`object`,...n.length>0?{required:n}:{},properties:e},t)}var Pe;(function(e){e[e.Array=0]=`Array`,e[e.BigInt=1]=`BigInt`,e[e.Boolean=2]=`Boolean`,e[e.Date=3]=`Date`,e[e.Constructor=4]=`Constructor`,e[e.Function=5]=`Function`,e[e.Null=6]=`Null`,e[e.Number=7]=`Number`,e[e.Object=8]=`Object`,e[e.RegExp=9]=`RegExp`,e[e.String=10]=`String`,e[e.Symbol=11]=`Symbol`,e[e.TypeArray=12]=`TypeArray`,e[e.Undefined=13]=`Undefined`})(Pe||={}),Array.from({length:256}).map((e,t)=>BigInt(t));var Fe=new Float64Array(1);new DataView(Fe.buffer),new Uint8Array(Fe.buffer),new TextEncoder;var Ie=`-?(?:0|[1-9][0-9]*)`,Le=`-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?`;function Re(e){return Ce({"~kind":`Number`},{type:`number`},e)}function D(e){return Ce({"~kind":`String`},{type:`string`},e)}var ze=`^${Ie}$`;`${Le}`;function Be(e,t){return Array.from({length:t-e+1},(t,n)=>String.fromCharCode(e+n))}var Ve=[...Be(97,122),...Be(65,90)],He=[`0`,...Be(49,57)];[...He],[...Ve,...He],[...He],new RegExp(ze);function Ue(e,t){try{return Promise.resolve(t(We))}catch(e){return Promise.reject(e)}}var We={startSpan:Ue,addEvent:()=>{},setAttributes:()=>{},setStatus:()=>{}};Object.freeze(We);var Ge=Symbol.for(`yaml.alias`),Ke=Symbol.for(`yaml.document`),O=Symbol.for(`yaml.map`),qe=Symbol.for(`yaml.pair`),Je=Symbol.for(`yaml.scalar`),Ye=Symbol.for(`yaml.seq`),k=Symbol.for(`yaml.node.type`),A=e=>!!e&&typeof e==`object`&&e[k]===Ge,Xe=e=>!!e&&typeof e==`object`&&e[k]===Ke,Ze=e=>!!e&&typeof e==`object`&&e[k]===O,j=e=>!!e&&typeof e==`object`&&e[k]===qe,M=e=>!!e&&typeof e==`object`&&e[k]===Je,Qe=e=>!!e&&typeof e==`object`&&e[k]===Ye;function N(e){if(e&&typeof e==`object`)switch(e[k]){case O:case Ye:return!0}return!1}function P(e){if(e&&typeof e==`object`)switch(e[k]){case Ge:case O:case Je:case Ye:return!0}return!1}var $e=e=>(M(e)||N(e))&&!!e.anchor,F=Symbol(`break visit`),et=Symbol(`skip children`),tt=Symbol(`remove node`);function nt(e,t){let n=rt(t);Xe(e)?I(null,e.contents,n,Object.freeze([e]))===tt&&(e.contents=null):I(null,e,n,Object.freeze([]))}nt.BREAK=F,nt.SKIP=et,nt.REMOVE=tt;function I(e,t,n,r){let i=it(e,t,n,r);if(P(i)||j(i))return at(e,r,i),I(e,i,n,r);if(typeof i!=`symbol`){if(N(t)){r=Object.freeze(r.concat(t));for(let e=0;e<t.items.length;++e){let i=I(e,t.items[e],n,r);if(typeof i==`number`)e=i-1;else if(i===F)return F;else i===tt&&(t.items.splice(e,1),--e)}}else if(j(t)){r=Object.freeze(r.concat(t));let e=I(`key`,t.key,n,r);if(e===F)return F;e===tt&&(t.key=null);let i=I(`value`,t.value,n,r);if(i===F)return F;i===tt&&(t.value=null)}}return i}function rt(e){return typeof e==`object`&&(e.Collection||e.Node||e.Value)?Object.assign({Alias:e.Node,Map:e.Node,Scalar:e.Node,Seq:e.Node},e.Value&&{Map:e.Value,Scalar:e.Value,Seq:e.Value},e.Collection&&{Map:e.Collection,Seq:e.Collection},e):e}function it(e,t,n,r){if(typeof n==`function`)return n(e,t,r);if(Ze(t))return n.Map?.(e,t,r);if(Qe(t))return n.Seq?.(e,t,r);if(j(t))return n.Pair?.(e,t,r);if(M(t))return n.Scalar?.(e,t,r);if(A(t))return n.Alias?.(e,t,r)}function at(e,t,n){let r=t[t.length-1];if(N(r))r.items[e]=n;else if(j(r))e===`key`?r.key=n:r.value=n;else if(Xe(r))r.contents=n;else{let e=A(r)?`alias`:`scalar`;throw Error(`Cannot replace node with ${e} parent`)}}function ot(e){if(/[\x00-\x19\s,[\]{}]/.test(e)){let t=`Anchor must not contain whitespace or control characters: ${JSON.stringify(e)}`;throw Error(t)}return!0}function st(e,t,n,r){if(r&&typeof r==`object`){if(Array.isArray(r))for(let t=0,n=r.length;t<n;++t){let n=r[t],i=st(e,r,String(t),n);i===void 0?delete r[t]:i!==n&&(r[t]=i)}else if(r instanceof Map)for(let t of Array.from(r.keys())){let n=r.get(t),i=st(e,r,t,n);i===void 0?r.delete(t):i!==n&&r.set(t,i)}else if(r instanceof Set)for(let t of Array.from(r)){let n=st(e,r,t,t);n===void 0?r.delete(t):n!==t&&(r.delete(t),r.add(n))}else for(let[t,n]of Object.entries(r)){let i=st(e,r,t,n);i===void 0?delete r[t]:i!==n&&(r[t]=i)}}return e.call(t,n,r)}function L(e,t,n){if(Array.isArray(e))return e.map((e,t)=>L(e,String(t),n));if(e&&typeof e.toJSON==`function`){if(!n||!$e(e))return e.toJSON(t,n);let r={aliasCount:0,count:1,res:void 0};n.anchors.set(e,r),n.onCreate=e=>{r.res=e,delete n.onCreate};let i=e.toJSON(t,n);return n.onCreate&&n.onCreate(i),i}return typeof e==`bigint`&&!n?.keep?Number(e):e}var ct=class{constructor(e){Object.defineProperty(this,k,{value:e})}clone(){let e=Object.create(Object.getPrototypeOf(this),Object.getOwnPropertyDescriptors(this));return this.range&&(e.range=this.range.slice()),e}toJS(e,{mapAsMap:t,maxAliasCount:n,onAnchor:r,reviver:i}={}){if(!Xe(e))throw TypeError(`A document argument is required`);let a={anchors:new Map,doc:e,keep:!0,mapAsMap:t===!0,mapKeyWarned:!1,maxAliasCount:typeof n==`number`?n:100},o=L(this,``,a);if(typeof r==`function`)for(let{count:e,res:t}of a.anchors.values())r(t,e);return typeof i==`function`?st(i,{"":o},``,o):o}},lt=class extends ct{constructor(e){super(Ge),this.source=e,Object.defineProperty(this,"tag",{set(){throw Error(`Alias nodes cannot have tags`)}})}resolve(e,t){if(t?.maxAliasCount===0)throw ReferenceError(`Alias resolution is disabled`);let n;t?.aliasResolveCache?n=t.aliasResolveCache:(n=[],nt(e,{Node:(e,t)=>{(A(t)||$e(t))&&n.push(t)}}),t&&(t.aliasResolveCache=n));let r;for(let e of n){if(e===this)break;e.anchor===this.source&&(r=e)}return r}toJSON(e,t){if(!t)return{source:this.source};let{anchors:n,doc:r,maxAliasCount:i}=t,a=this.resolve(r,t);if(!a){let e=`Unresolved alias (the anchor must be set before the alias): ${this.source}`;throw ReferenceError(e)}let o=n.get(a);if(o||=(L(a,null,t),n.get(a)),o?.res===void 0)throw ReferenceError(`This should not happen: Alias anchor was not resolved?`);if(i>=0&&(o.count+=1,o.aliasCount===0&&(o.aliasCount=ut(r,a,n)),o.count*o.aliasCount>i))throw ReferenceError(`Excessive alias count indicates a resource exhaustion attack`);return o.res}toString(e,t,n){let r=`*${this.source}`;if(e){if(ot(this.source),e.options.verifyAliasOrder&&!e.anchors.has(this.source)){let e=`Unresolved alias (the anchor must be set before the alias): ${this.source}`;throw Error(e)}if(e.implicitKey)return`${r} `}return r}};function ut(e,t,n){if(A(t)){let r=t.resolve(e),i=n&&r&&n.get(r);return i?i.count*i.aliasCount:0}if(N(t)){let r=0;for(let i of t.items){let t=ut(e,i,n);t>r&&(r=t)}return r}if(j(t)){let r=ut(e,t.key,n),i=ut(e,t.value,n);return Math.max(r,i)}return 1}var dt=e=>!e||typeof e!=`function`&&typeof e!=`object`,R=class extends ct{constructor(e){super(Je),this.value=e}toJSON(e,t){return t?.keep?this.value:L(this.value,e,t)}toString(){return String(this.value)}};R.BLOCK_FOLDED=`BLOCK_FOLDED`,R.BLOCK_LITERAL=`BLOCK_LITERAL`,R.PLAIN=`PLAIN`,R.QUOTE_DOUBLE=`QUOTE_DOUBLE`,R.QUOTE_SINGLE=`QUOTE_SINGLE`;var ft=`tag:yaml.org,2002:`;function pt(e,t,n){if(t){let e=n.filter(e=>e.tag===t),r=e.find(e=>!e.format)??e[0];if(!r)throw Error(`Tag ${t} not found`);return r}return n.find(t=>t.identify?.(e)&&!t.format)}function mt(e,t,n){if(Xe(e)&&(e=e.contents),P(e))return e;if(j(e)){let t=n.schema[O].createNode?.(n.schema,null,n);return t.items.push(e),t}(e instanceof String||e instanceof Number||e instanceof Boolean||typeof BigInt<`u`&&e instanceof BigInt)&&(e=e.valueOf());let{aliasDuplicateObjects:r,onAnchor:i,onTagObj:a,schema:o,sourceObjects:s}=n,c;if(r&&e&&typeof e==`object`){if(c=s.get(e),c)return c.anchor??(c.anchor=i(e)),new lt(c.anchor);c={anchor:null,node:null},s.set(e,c)}t?.startsWith(`!!`)&&(t=ft+t.slice(2));let l=pt(e,t,o.tags);if(!l){if(e&&typeof e.toJSON==`function`&&(e=e.toJSON()),!e||typeof e!=`object`){let t=new R(e);return c&&(c.node=t),t}l=e instanceof Map?o[O]:Symbol.iterator in Object(e)?o[Ye]:o[O]}a&&(a(l),delete n.onTagObj);let u=l?.createNode?l.createNode(n.schema,e,n):typeof l?.nodeClass?.from==`function`?l.nodeClass.from(n.schema,e,n):new R(e);return t?u.tag=t:l.default||(u.tag=l.tag),c&&(c.node=u),u}function ht(e,t,n){let r=n;for(let e=t.length-1;e>=0;--e){let n=t[e];if(typeof n==`number`&&Number.isInteger(n)&&n>=0){let e=[];e[n]=r,r=e}else r=new Map([[n,r]])}return mt(r,void 0,{aliasDuplicateObjects:!1,keepUndefined:!1,onAnchor:()=>{throw Error(`This should not happen, please report a bug.`)},schema:e,sourceObjects:new Map})}var gt=e=>e==null||typeof e==`object`&&!!e[Symbol.iterator]().next().done,_t=class extends ct{constructor(e,t){super(e),Object.defineProperty(this,"schema",{value:t,configurable:!0,enumerable:!1,writable:!0})}clone(e){let t=Object.create(Object.getPrototypeOf(this),Object.getOwnPropertyDescriptors(this));return e&&(t.schema=e),t.items=t.items.map(t=>P(t)||j(t)?t.clone(e):t),this.range&&(t.range=this.range.slice()),t}addIn(e,t){if(gt(e))this.add(t);else{let[n,...r]=e,i=this.get(n,!0);if(N(i))i.addIn(r,t);else if(i===void 0&&this.schema)this.set(n,ht(this.schema,r,t));else throw Error(`Expected YAML collection at ${n}. Remaining path: ${r}`)}}deleteIn(e){let[t,...n]=e;if(n.length===0)return this.delete(t);let r=this.get(t,!0);if(N(r))return r.deleteIn(n);throw Error(`Expected YAML collection at ${t}. Remaining path: ${n}`)}getIn(e,t){let[n,...r]=e,i=this.get(n,!0);return r.length===0?!t&&M(i)?i.value:i:N(i)?i.getIn(r,t):void 0}hasAllNullValues(e){return this.items.every(t=>{if(!j(t))return!1;let n=t.value;return n==null||e&&M(n)&&n.value==null&&!n.commentBefore&&!n.comment&&!n.tag})}hasIn(e){let[t,...n]=e;if(n.length===0)return this.has(t);let r=this.get(t,!0);return N(r)?r.hasIn(n):!1}setIn(e,t){let[n,...r]=e;if(r.length===0)this.set(n,t);else{let e=this.get(n,!0);if(N(e))e.setIn(r,t);else if(e===void 0&&this.schema)this.set(n,ht(this.schema,r,t));else throw Error(`Expected YAML collection at ${n}. Remaining path: ${r}`)}}},vt=e=>e.replace(/^(?!$)(?: $)?/gm,`#`);function yt(e,t){return/^\n+$/.test(e)?e.substring(1):t?e.replace(/^(?! *$)/gm,t):e}var z=(e,t,n)=>e.endsWith(`
|
|
3
3
|
`)?yt(n,t):n.includes(`
|
|
4
4
|
`)?`
|
|
5
5
|
`+yt(n,t):(e.endsWith(` `)?``:` `)+n,bt=`flow`,xt=`block`,St=`quoted`;function Ct(e,t,n=`flow`,{indentAtStart:r,lineWidth:i=80,minContentWidth:a=20,onFold:o,onOverflow:s}={}){if(!i||i<0)return e;i<a&&(a=0);let c=Math.max(1+a,1+i-t.length);if(e.length<=c)return e;let l=[],u={},d=i-t.length;typeof r==`number`&&(r>i-Math.max(2,a)?l.push(0):d=i-r);let f,p,m=!1,h=-1,g=-1,_=-1;n===`block`&&(h=wt(e,h,t.length),h!==-1&&(d=h+c));for(let r;r=e[h+=1];){if(n===`quoted`&&r===`\\`){switch(g=h,e[h+1]){case`x`:h+=3;break;case`u`:h+=5;break;case`U`:h+=9;break;default:h+=1}_=h}if(r===`
|
|
@@ -1 +1 @@
|
|
|
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-95TyImtj.js";import"./src-CcFD5QfS.js";import"./proxied-fetch-BduXImRw.js";import{i as s,t as c}from"./api-endpoint-C00IT5T9.js";import{i as ee,o as te}from"./core-DpOt0gTq.js";import{i as l,n as ne,r as u}from"./compat-Dujkqs4W.js";import{r as re,t as ie}from"./bedrock-camp-compat-CYnC9BQI.js";import{a as d}from"./claude-model-version-CJiITCDG.js";const f=`slicc_oauth_extra_domains`;function p(e){try{let t=e.getItem(f);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 ae(e,t){try{e.setItem(f,JSON.stringify(t))}catch(e){throw Error(`Failed to persist OAuth extras (localStorage quota exceeded?): ${e instanceof Error?e.message:String(e)}`)}}function oe(e,t){let n={input:0,output:0,cacheRead:0,cacheWrite:0},r=d(e);if(!r)return n;let i;for(let e of t.values()){let t=d(e.id);!t||t.family!==r.family||(!i||t.major>i.major||t.major===i.major&&t.minor>i.minor)&&(i={major:t.major,minor:t.minor,cost:e.cost})}return i?.cost??n}const se=t(`model-policy`),m=`/etc/models`;function h(){return{sections:{}}}function ce(e){let t=e.indexOf(`#`);return(t===-1?e:e.slice(0,t)).trim()}function le(e){let t=h();if(typeof e!=`string`)return t;let n=null;for(let r of e.split(/\r?\n/)){let e=ce(r);if(!e)continue;let i=/^\[([^\]]+)\]$/.exec(e);if(i){n=i[1].trim(),t.sections[n]??=[];continue}if(n===null){se.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){se.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 ue(e,t){return e.sections[t]??[]}function de(e,t,n,r){return ue(e,t).some(e=>e.deny&&g(e,n,r))}function fe(e,t,n,r){let i=ue(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 pe=h();function me(e){pe=e}function _(){return pe}function he(e,t,n){return`add \`${t}:${n}\` (or \`${t}:*\`) under \`[${e}]\` in ${m}`}var ge=e({ACCOUNTS_KEY:()=>b,__test__:()=>Se,addAccount:()=>I,applyProviderDefaults:()=>Te,clearAllSettings:()=>Ze,clearApiKey:()=>Ke,clearBaseUrl:()=>Ye,clearSelectedProvider:()=>Ue,exportProviders:()=>Xe,getAccounts:()=>N,getAllAvailableModels:()=>Oe,getAllExtraOAuthDomains:()=>je,getApiKey:()=>We,getApiKeyForProvider:()=>B,getApiVersionForProvider:()=>ze,getAvailableProviders:()=>w,getBaseUrl:()=>qe,getBaseUrlForProvider:()=>V,getDeploymentForProvider:()=>Re,getExtraOAuthDomains:()=>P,getOAuthAccountInfo:()=>k,getProviderConfig:()=>E,getProviderModels:()=>O,getRawApiKeyForProvider:()=>z,getSelectedModelId:()=>H,getSelectedProvider:()=>U,isModelHiddenFromPicker:()=>j,logoutOAuthAccount:()=>Pe,maskOAuthTokenWithRetry:()=>Fe,migrateLegacyAuthOnlySelection:()=>be,modelRunsOnProvider:()=>q,persistOAuthMaskViaServiceWorker:()=>R,policyVisible:()=>M,providerOffersLlmModels:()=>T,removeAccount:()=>L,resolveCurrentModel:()=>J,resolveModelById:()=>K,resolveModelByShorthand:()=>nt,resolveModelIdForScoop:()=>ut,resolveModelSelectionForScoop:()=>Q,saveOAuthAccount:()=>Le,setApiKey:()=>Ge,setBaseUrl:()=>Je,setExtraOAuthDomains:()=>ke,setExtraOAuthDomainsAsync:()=>Ae,setSelectedModelId:()=>Be,setSelectedProvider:()=>He});const v=ne,y=u,b=`slicc_accounts`,x=`selected-model`,_e=[`slicc_provider`,`slicc_api_key`,`slicc_base_url`,`anthropic_api_key`,`api_provider`,`azure_resource`,`bedrock_region`],ve=new Set([`github`]);function S(e){let t;try{t=l()}catch{return!1}return t.includes(e)&&!a(e)}let C=!1;function ye(){if(!C){C=!0;for(let e of _e)try{localStorage.removeItem(e)}catch{}be()}}function be(){try{let e=localStorage.getItem(x);if(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t);(ve.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 xe(){C=!1}const Se={_resetLegacyCleanup:xe};function w(){let e=l().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(u(e).length>0)return!0}catch{}let t=o(e);return!!(t?.modelOverrides&&Object.keys(t.modelOverrides).length>0)}function Ce(e,t){try{if(O(e).some(e=>e.id===t))return!0}catch{}try{if(u(e).some(e=>e.id===t))return!0}catch{}let n=o(e);return!!(n?.modelOverrides&&Object.hasOwn(n.modelOverrides,t))}function E(e){return o(e)||{id:e,name:e.split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `),description:`${e} provider`,requiresApiKey:!0,requiresBaseUrl:!1}}function D(e,t){t.context_window!==void 0&&(e.contextWindow=t.context_window),t.max_tokens!==void 0&&(e.maxTokens=t.max_tokens),t.reasoning!==void 0&&(e.reasoning=t.reasoning),t.input!==void 0&&(e.input=t.input),t.cost!==void 0&&(e.cost=t.cost,e.inputCost=t.cost.input,e.outputCost=t.cost.output,e.cacheReadCost=t.cost.cacheRead,e.cacheWriteCost=t.cost.cacheWrite),t.compat!==void 0&&(e.compat={...e.compat??{},...t.compat}),t.thinkingLevelMap!==void 0&&(e.thinkingLevelMap={...e.thinkingLevelMap??{},...t.thinkingLevelMap})}function O(e){try{if(e===`bedrock-camp`){let e=ie(V(`bedrock-camp`));return y(`amazon-bedrock`).filter(t=>re(t,e)).map(e=>({...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`}))}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 l())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=W(e,n.id,``,a),n.name&&(s.name=n.name),i===`anthropic`){let e=oe(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=N().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 we=Object.assign({})[`/packages/webapp/providers.json`]??[],A=t(`provider-settings`);function Te(e=we){if(e.length===0||N().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}I(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 Ee=[/haiku/i];function j(e){return Ee.some(t=>t.test(e))}function De(e){return e.filter(e=>!j(e.id))}function M(e,t){let n=$();if(n===null)return e;let r=_();return e.filter(e=>!de(r,n,t,e.id))}function Oe(){let e=N();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=M(De(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 N(){ye();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 P(e){return p(localStorage)[e]??[]}function ke(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,ae(localStorage,n)}async function Ae(e,t){if(r()){ke(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{ae(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 je(){return p(localStorage)}function Me(e){localStorage.setItem(b,JSON.stringify(e))}async function F(e){if(r()){Me(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 I(e,t,n,r,i){let a=N().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),Me(a)}async function Ne(e){let t=te();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 ee(`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 L(e){let t=N().find(t=>t.providerId===e),n=E(e);t&&n?.isOAuth&&await Pe(e),await Ne(e),await F(N().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 Pe(e){let t=N().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-CUpgufjo.js`);await t(r).catch(t=>{A.warn(`IdP logout popup failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})})}}await F(N().map(t=>t.providerId===e?{providerId:t.providerId,apiKey:``,baseUrl:t.baseUrl,userName:t.userName,userAvatar:t.userAvatar,loggedOut:!0}:t)),await Ne(e)}}async function Fe(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}}async function R(e,t,n){let r={providerId:e.providerId,accessToken:e.accessToken,domains:e.domains.join(`,`)},{maskedValue:i,lastError:a}=await Fe(()=>t.sendMaskRequest(r),n);if(!i){A.error(`OAuth mask give-up: no masked value after retries`,{providerId:e.providerId,reason:a??`no error reported (cold SW or empty reply)`});return}let o=t.getAccounts(),s=o.find(t=>t.providerId===e.providerId);s&&(s.maskedValue=i,await t.saveAccounts(o))}function Ie(e,t){if(e.accessToken)return`scopes`in e?e.scopes:t?.scopes}async function Le(e){let t=N().find(t=>t.providerId===e.providerId),n=N().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:Ie(e,t)}),await F(n);let r=E(e.providerId)?.oauthTokenDomains??[],i=P(e.providerId),a=new Set,o=[];for(let e of[...r,...i]){let t=e.toLowerCase();a.has(t)||(a.add(t),o.push(e))}if(o.length===0)return;let l=te();try{if(l===`extension-direct`)await R({providerId:e.providerId,accessToken:e.accessToken,domains:o},{sendMaskRequest:t=>new Promise(n=>{chrome.runtime.sendMessage({type:`secrets.mask-oauth-token`,...t},t=>{chrome.runtime.lastError&&A.error(`SW mask-oauth-token transport failed`,{providerId:e.providerId,error:chrome.runtime.lastError.message});let r=typeof t==`object`&&t?t:void 0;r?.error&&A.warn(`SW mask-oauth-token returned error`,{providerId:e.providerId,error:r.error}),n(r??{})})}),getAccounts:N,saveAccounts:F});else if(l===`extension-delegate`)await R({providerId:e.providerId,accessToken:e.accessToken,domains:o},{sendMaskRequest:t=>ee(`secrets.mask-oauth-token`,t).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)}),{})),getAccounts:N,saveAccounts:F});else if(l!==`connect`){let t=await fetch(s(`/api/secrets/oauth-update`),{method:`POST`,headers:c({"Content-Type":`application/json`}),body:JSON.stringify({providerId:e.providerId,accessToken:e.accessToken,domains:o})});if(t.ok){let n=await t.json();if(typeof n.maskedValue==`string`){let t=N(),r=t.find(t=>t.providerId===e.providerId);r&&(r.maskedValue=n.maskedValue,await F(t))}else A.warn(`OAuth replica POST ok but missing maskedValue`,{providerId:e.providerId})}else A.warn(`OAuth replica POST non-ok`,{providerId:e.providerId,status:t.status})}}catch(t){A.error(`OAuth replica sync failed`,{providerId:e.providerId,topology:l,error:t instanceof Error?t.message:String(t)})}}function z(e){let t=N().find(t=>t.providerId===e);return t&&(t.accessToken||t.apiKey)||null}function B(e){let t=N().find(t=>t.providerId===e);return t?t.accessToken||t.apiKey||(E(e).optionalApiKey?`local`:null):null}function V(e){return N().find(t=>t.providerId===e)?.baseUrl??null}function Re(e){return N().find(t=>t.providerId===e)?.deployment??null}function ze(e){return N().find(t=>t.providerId===e)?.apiVersion??null}function H(){let e=localStorage.getItem(x)||``,t=e.indexOf(`:`);return t>=0?e.slice(t+1):e}function Be(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=U();localStorage.setItem(x,`${n}:${e}`)}function Ve(){return localStorage.getItem(x)||``}function U(){let e=Ve(),t=e.indexOf(`:`);if(t>0)return e.slice(0,t);let n=N(),r=H();if(r){let e=n.find(e=>T(e.providerId)&&Ce(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 He(e){let t=H();localStorage.setItem(x,`${e}:${t}`)}function Ue(){let e=H();localStorage.setItem(x,e)}function We(){return B(U())}function Ge(e){let t=U();I(t,e,V(t)??void 0)}async function Ke(){await L(U())}function qe(){return V(U())}function Je(e){let t=U(),n=z(t);n&&I(t,n,e||void 0)}function Ye(){let e=U(),t=z(e);t&&I(e,t)}function Xe(){let e=N(),t=U(),n=H();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 Ze(){let e=N();await Promise.allSettled(e.map(e=>L(e.providerId))),localStorage.removeItem(b),localStorage.removeItem(x);for(let e of _e)localStorage.removeItem(e)}function Qe(e){return/^(?:gpt[-.]?|o[0-9]|chatgpt)/i.test(e)?`openai`:`anthropic`}function W(e,t,n,r){return{id:t,name:t,provider:e,api:r??`${e}-${Qe(t)}`,baseUrl:n??``,contextWindow:2e5,maxTokens:16384,input:[`text`,`image`],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},reasoning:!0}}function G(e,t){return t.isOAuth||e===`azure-ai-foundry`?`anthropic`:e===`bedrock-camp`?`amazon-bedrock`:e}function $e(e,t,n){let r=O(e).find(e=>e.id===t);return r?n?{...r,baseUrl:n}:r:null}function et(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 tt(e,t,n,r,i){if(t.isOAuth)return $e(e,n,r)??W(e,n,r);if(i)throw Error(`Model ${n} is not available from provider ${e}`);return J()}function K(e,t){if(!e)return J();let n=t??U(),r=V(n),i=E(n);if(t!==void 0){let t=$e(n,e,r);if(t)return t}try{let t=G(n,i),a=v(t,e);if(!a?.id)throw Error(`Model ${e} not found`);let o=et(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)}),tt(n,i,e,r,t!==void 0)}}function q(e,t){return e.provider===t||e.provider===G(t,E(t))}function J(){let e=U(),t=H(),n=V(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=G(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?W(e,o,n):v(`anthropic`,`claude-sonnet-4-0`)}}function nt(e){let t=e.toLowerCase();if(!t)return null;let n=null;try{n=U()}catch{}if(n!==null){let e=Y(t,[n]);if(e!==null)return e}return Y(t,N().map(e=>e.providerId))}function Y(e,t){let n=null,r=-1;for(let i of t)for(let t of O(i)){let i=t.id.toLowerCase(),a=(t.name??``).toLowerCase();if(!i.includes(e)&&!a.includes(e))continue;let o=t.contextWindow??0;(o>r||o===r&&dt(t.id,n??``)>0)&&(r=o,n=t.id)}return n}function X(){let e=[];try{for(let t of N())e.includes(t.providerId)||e.push(t.providerId)}catch{}try{let t=U();e.includes(t)||e.push(t)}catch{}return e}function rt(e){let t=e.indexOf(`:`);if(t<=0||t===e.length-1)return null;let n=e.slice(0,t),r=e.slice(t+1),i=X().includes(n);if(i)return{providerId:n,modelId:r,configured:i};let a=!1;try{a=w().includes(n)}catch{}return a?{providerId:n,modelId:r,configured:!1}:null}function Z(e,t,n=!0){let r=[e],i=n?Y(e.toLowerCase(),[t]):null;i!==null&&i!==e&&r.push(i);let a=[];try{a=O(t)}catch{}for(let n of r)if(!(a.length>0&&!a.some(e=>e.id===n)))try{if(K(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 it(e){return e.map(e=>`${e.providerId}:${e.modelId}`).join(`, `)}function Q(e){if(!e)return{ok:!1,error:`unknown model: (empty)`};let t=rt(e);if(t)return ct(e,t);let n=null;try{n=U()}catch{}if(n!==null){let t=Z(e,n);if(t)return st(t)}return lt(e,n)}function $(){try{return U()}catch{return null}}function at(e){let t=$();return t===null||fe(_(),t,e.providerId,e.modelId)}function ot(e){let t=$()??`<none>`;return{ok:!1,error:`model not allowed: ${`${e.providerId}:${e.modelId}`} is blocked by ${m} while "${t}" is selected — ${he(t,e.providerId,e.modelId)}`}}function st(e){return at(e)?{ok:!0,selection:e}:ot(e)}function ct(e,t){if(!t.configured){let n=X();return{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" is not configured${n.length>0?`; configured: ${n.join(`, `)}`:``})`}}let n=Z(t.modelId,t.providerId);return n?st(n):{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" does not offer "${t.modelId}")`}}function lt(e,t){let n=X().filter(e=>e!==t);for(let t of[`exact`,`shorthand`]){let r=[];for(let i of n){let n=t===`exact`?e:Y(e.toLowerCase(),[i]);if(n===null)continue;let a=Z(n,i,!1);a&&r.push(a)}let i=r.filter(at);if(i.length===0&&r.length>0)return ot(r[0]);if(i.length===1)return{ok:!0,selection:i[0]};if(i.length>1)return{ok:!1,error:`ambiguous model: ${e} matches ${it(i)} — qualify it as provider:model`}}return{ok:!1,error:`unknown model: ${e}`}}function ut(e){let t=Q(e);return t.ok?t.selection.modelId:null}function dt(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{m as _,ze as a,me as b,k as c,U as d,q as f,Le as g,Q as h,B as i,O as l,K as m,N as n,V as o,J as p,We as r,Re as s,ge as t,H as u,h as v,oe as x,le as y};
|
|
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-DuNEVEmM.js";import"./src-CcFD5QfS.js";import"./proxied-fetch-BduXImRw.js";import{i as s,t as c}from"./api-endpoint-C00IT5T9.js";import{i as ee,o as te}from"./core-DpOt0gTq.js";import{i as l,n as ne,r as u}from"./compat-Dujkqs4W.js";import{r as re,t as ie}from"./bedrock-camp-compat-CYnC9BQI.js";import{a as d}from"./claude-model-version-CJiITCDG.js";const f=`slicc_oauth_extra_domains`;function p(e){try{let t=e.getItem(f);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 ae(e,t){try{e.setItem(f,JSON.stringify(t))}catch(e){throw Error(`Failed to persist OAuth extras (localStorage quota exceeded?): ${e instanceof Error?e.message:String(e)}`)}}function oe(e,t){let n={input:0,output:0,cacheRead:0,cacheWrite:0},r=d(e);if(!r)return n;let i;for(let e of t.values()){let t=d(e.id);!t||t.family!==r.family||(!i||t.major>i.major||t.major===i.major&&t.minor>i.minor)&&(i={major:t.major,minor:t.minor,cost:e.cost})}return i?.cost??n}const se=t(`model-policy`),m=`/etc/models`;function h(){return{sections:{}}}function ce(e){let t=e.indexOf(`#`);return(t===-1?e:e.slice(0,t)).trim()}function le(e){let t=h();if(typeof e!=`string`)return t;let n=null;for(let r of e.split(/\r?\n/)){let e=ce(r);if(!e)continue;let i=/^\[([^\]]+)\]$/.exec(e);if(i){n=i[1].trim(),t.sections[n]??=[];continue}if(n===null){se.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){se.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 ue(e,t){return e.sections[t]??[]}function de(e,t,n,r){return ue(e,t).some(e=>e.deny&&g(e,n,r))}function fe(e,t,n,r){let i=ue(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 pe=h();function me(e){pe=e}function _(){return pe}function he(e,t,n){return`add \`${t}:${n}\` (or \`${t}:*\`) under \`[${e}]\` in ${m}`}var ge=e({ACCOUNTS_KEY:()=>b,__test__:()=>Se,addAccount:()=>I,applyProviderDefaults:()=>Te,clearAllSettings:()=>Ze,clearApiKey:()=>Ke,clearBaseUrl:()=>Ye,clearSelectedProvider:()=>Ue,exportProviders:()=>Xe,getAccounts:()=>N,getAllAvailableModels:()=>Oe,getAllExtraOAuthDomains:()=>je,getApiKey:()=>We,getApiKeyForProvider:()=>B,getApiVersionForProvider:()=>ze,getAvailableProviders:()=>w,getBaseUrl:()=>qe,getBaseUrlForProvider:()=>V,getDeploymentForProvider:()=>Re,getExtraOAuthDomains:()=>P,getOAuthAccountInfo:()=>k,getProviderConfig:()=>E,getProviderModels:()=>O,getRawApiKeyForProvider:()=>z,getSelectedModelId:()=>H,getSelectedProvider:()=>U,isModelHiddenFromPicker:()=>j,logoutOAuthAccount:()=>Pe,maskOAuthTokenWithRetry:()=>Fe,migrateLegacyAuthOnlySelection:()=>be,modelRunsOnProvider:()=>q,persistOAuthMaskViaServiceWorker:()=>R,policyVisible:()=>M,providerOffersLlmModels:()=>T,removeAccount:()=>L,resolveCurrentModel:()=>J,resolveModelById:()=>K,resolveModelByShorthand:()=>nt,resolveModelIdForScoop:()=>ut,resolveModelSelectionForScoop:()=>Q,saveOAuthAccount:()=>Le,setApiKey:()=>Ge,setBaseUrl:()=>Je,setExtraOAuthDomains:()=>ke,setExtraOAuthDomainsAsync:()=>Ae,setSelectedModelId:()=>Be,setSelectedProvider:()=>He});const v=ne,y=u,b=`slicc_accounts`,x=`selected-model`,_e=[`slicc_provider`,`slicc_api_key`,`slicc_base_url`,`anthropic_api_key`,`api_provider`,`azure_resource`,`bedrock_region`],ve=new Set([`github`]);function S(e){let t;try{t=l()}catch{return!1}return t.includes(e)&&!a(e)}let C=!1;function ye(){if(!C){C=!0;for(let e of _e)try{localStorage.removeItem(e)}catch{}be()}}function be(){try{let e=localStorage.getItem(x);if(e){let t=e.indexOf(`:`);if(t>0){let n=e.slice(0,t);(ve.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 xe(){C=!1}const Se={_resetLegacyCleanup:xe};function w(){let e=l().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(u(e).length>0)return!0}catch{}let t=o(e);return!!(t?.modelOverrides&&Object.keys(t.modelOverrides).length>0)}function Ce(e,t){try{if(O(e).some(e=>e.id===t))return!0}catch{}try{if(u(e).some(e=>e.id===t))return!0}catch{}let n=o(e);return!!(n?.modelOverrides&&Object.hasOwn(n.modelOverrides,t))}function E(e){return o(e)||{id:e,name:e.split(`-`).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `),description:`${e} provider`,requiresApiKey:!0,requiresBaseUrl:!1}}function D(e,t){t.context_window!==void 0&&(e.contextWindow=t.context_window),t.max_tokens!==void 0&&(e.maxTokens=t.max_tokens),t.reasoning!==void 0&&(e.reasoning=t.reasoning),t.input!==void 0&&(e.input=t.input),t.cost!==void 0&&(e.cost=t.cost,e.inputCost=t.cost.input,e.outputCost=t.cost.output,e.cacheReadCost=t.cost.cacheRead,e.cacheWriteCost=t.cost.cacheWrite),t.compat!==void 0&&(e.compat={...e.compat??{},...t.compat}),t.thinkingLevelMap!==void 0&&(e.thinkingLevelMap={...e.thinkingLevelMap??{},...t.thinkingLevelMap})}function O(e){try{if(e===`bedrock-camp`){let e=ie(V(`bedrock-camp`));return y(`amazon-bedrock`).filter(t=>re(t,e)).map(e=>({...e,api:`bedrock-camp-converse`,provider:`bedrock-camp`}))}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 l())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=W(e,n.id,``,a),n.name&&(s.name=n.name),i===`anthropic`){let e=oe(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=N().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 we=Object.assign({})[`/packages/webapp/providers.json`]??[],A=t(`provider-settings`);function Te(e=we){if(e.length===0||N().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}I(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 Ee=[/haiku/i];function j(e){return Ee.some(t=>t.test(e))}function De(e){return e.filter(e=>!j(e.id))}function M(e,t){let n=$();if(n===null)return e;let r=_();return e.filter(e=>!de(r,n,t,e.id))}function Oe(){let e=N();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=M(De(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 N(){ye();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 P(e){return p(localStorage)[e]??[]}function ke(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,ae(localStorage,n)}async function Ae(e,t){if(r()){ke(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{ae(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 je(){return p(localStorage)}function Me(e){localStorage.setItem(b,JSON.stringify(e))}async function F(e){if(r()){Me(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 I(e,t,n,r,i){let a=N().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),Me(a)}async function Ne(e){let t=te();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 ee(`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 L(e){let t=N().find(t=>t.providerId===e),n=E(e);t&&n?.isOAuth&&await Pe(e),await Ne(e),await F(N().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 Pe(e){let t=N().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-CUpgufjo.js`);await t(r).catch(t=>{A.warn(`IdP logout popup failed`,{providerId:e,error:t instanceof Error?t.message:String(t)})})}}await F(N().map(t=>t.providerId===e?{providerId:t.providerId,apiKey:``,baseUrl:t.baseUrl,userName:t.userName,userAvatar:t.userAvatar,loggedOut:!0}:t)),await Ne(e)}}async function Fe(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}}async function R(e,t,n){let r={providerId:e.providerId,accessToken:e.accessToken,domains:e.domains.join(`,`)},{maskedValue:i,lastError:a}=await Fe(()=>t.sendMaskRequest(r),n);if(!i){A.error(`OAuth mask give-up: no masked value after retries`,{providerId:e.providerId,reason:a??`no error reported (cold SW or empty reply)`});return}let o=t.getAccounts(),s=o.find(t=>t.providerId===e.providerId);s&&(s.maskedValue=i,await t.saveAccounts(o))}function Ie(e,t){if(e.accessToken)return`scopes`in e?e.scopes:t?.scopes}async function Le(e){let t=N().find(t=>t.providerId===e.providerId),n=N().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:Ie(e,t)}),await F(n);let r=E(e.providerId)?.oauthTokenDomains??[],i=P(e.providerId),a=new Set,o=[];for(let e of[...r,...i]){let t=e.toLowerCase();a.has(t)||(a.add(t),o.push(e))}if(o.length===0)return;let l=te();try{if(l===`extension-direct`)await R({providerId:e.providerId,accessToken:e.accessToken,domains:o},{sendMaskRequest:t=>new Promise(n=>{chrome.runtime.sendMessage({type:`secrets.mask-oauth-token`,...t},t=>{chrome.runtime.lastError&&A.error(`SW mask-oauth-token transport failed`,{providerId:e.providerId,error:chrome.runtime.lastError.message});let r=typeof t==`object`&&t?t:void 0;r?.error&&A.warn(`SW mask-oauth-token returned error`,{providerId:e.providerId,error:r.error}),n(r??{})})}),getAccounts:N,saveAccounts:F});else if(l===`extension-delegate`)await R({providerId:e.providerId,accessToken:e.accessToken,domains:o},{sendMaskRequest:t=>ee(`secrets.mask-oauth-token`,t).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)}),{})),getAccounts:N,saveAccounts:F});else if(l!==`connect`){let t=await fetch(s(`/api/secrets/oauth-update`),{method:`POST`,headers:c({"Content-Type":`application/json`}),body:JSON.stringify({providerId:e.providerId,accessToken:e.accessToken,domains:o})});if(t.ok){let n=await t.json();if(typeof n.maskedValue==`string`){let t=N(),r=t.find(t=>t.providerId===e.providerId);r&&(r.maskedValue=n.maskedValue,await F(t))}else A.warn(`OAuth replica POST ok but missing maskedValue`,{providerId:e.providerId})}else A.warn(`OAuth replica POST non-ok`,{providerId:e.providerId,status:t.status})}}catch(t){A.error(`OAuth replica sync failed`,{providerId:e.providerId,topology:l,error:t instanceof Error?t.message:String(t)})}}function z(e){let t=N().find(t=>t.providerId===e);return t&&(t.accessToken||t.apiKey)||null}function B(e){let t=N().find(t=>t.providerId===e);return t?t.accessToken||t.apiKey||(E(e).optionalApiKey?`local`:null):null}function V(e){return N().find(t=>t.providerId===e)?.baseUrl??null}function Re(e){return N().find(t=>t.providerId===e)?.deployment??null}function ze(e){return N().find(t=>t.providerId===e)?.apiVersion??null}function H(){let e=localStorage.getItem(x)||``,t=e.indexOf(`:`);return t>=0?e.slice(t+1):e}function Be(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=U();localStorage.setItem(x,`${n}:${e}`)}function Ve(){return localStorage.getItem(x)||``}function U(){let e=Ve(),t=e.indexOf(`:`);if(t>0)return e.slice(0,t);let n=N(),r=H();if(r){let e=n.find(e=>T(e.providerId)&&Ce(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 He(e){let t=H();localStorage.setItem(x,`${e}:${t}`)}function Ue(){let e=H();localStorage.setItem(x,e)}function We(){return B(U())}function Ge(e){let t=U();I(t,e,V(t)??void 0)}async function Ke(){await L(U())}function qe(){return V(U())}function Je(e){let t=U(),n=z(t);n&&I(t,n,e||void 0)}function Ye(){let e=U(),t=z(e);t&&I(e,t)}function Xe(){let e=N(),t=U(),n=H();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 Ze(){let e=N();await Promise.allSettled(e.map(e=>L(e.providerId))),localStorage.removeItem(b),localStorage.removeItem(x);for(let e of _e)localStorage.removeItem(e)}function Qe(e){return/^(?:gpt[-.]?|o[0-9]|chatgpt)/i.test(e)?`openai`:`anthropic`}function W(e,t,n,r){return{id:t,name:t,provider:e,api:r??`${e}-${Qe(t)}`,baseUrl:n??``,contextWindow:2e5,maxTokens:16384,input:[`text`,`image`],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},reasoning:!0}}function G(e,t){return t.isOAuth||e===`azure-ai-foundry`?`anthropic`:e===`bedrock-camp`?`amazon-bedrock`:e}function $e(e,t,n){let r=O(e).find(e=>e.id===t);return r?n?{...r,baseUrl:n}:r:null}function et(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 tt(e,t,n,r,i){if(t.isOAuth)return $e(e,n,r)??W(e,n,r);if(i)throw Error(`Model ${n} is not available from provider ${e}`);return J()}function K(e,t){if(!e)return J();let n=t??U(),r=V(n),i=E(n);if(t!==void 0){let t=$e(n,e,r);if(t)return t}try{let t=G(n,i),a=v(t,e);if(!a?.id)throw Error(`Model ${e} not found`);let o=et(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)}),tt(n,i,e,r,t!==void 0)}}function q(e,t){return e.provider===t||e.provider===G(t,E(t))}function J(){let e=U(),t=H(),n=V(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=G(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?W(e,o,n):v(`anthropic`,`claude-sonnet-4-0`)}}function nt(e){let t=e.toLowerCase();if(!t)return null;let n=null;try{n=U()}catch{}if(n!==null){let e=Y(t,[n]);if(e!==null)return e}return Y(t,N().map(e=>e.providerId))}function Y(e,t){let n=null,r=-1;for(let i of t)for(let t of O(i)){let i=t.id.toLowerCase(),a=(t.name??``).toLowerCase();if(!i.includes(e)&&!a.includes(e))continue;let o=t.contextWindow??0;(o>r||o===r&&dt(t.id,n??``)>0)&&(r=o,n=t.id)}return n}function X(){let e=[];try{for(let t of N())e.includes(t.providerId)||e.push(t.providerId)}catch{}try{let t=U();e.includes(t)||e.push(t)}catch{}return e}function rt(e){let t=e.indexOf(`:`);if(t<=0||t===e.length-1)return null;let n=e.slice(0,t),r=e.slice(t+1),i=X().includes(n);if(i)return{providerId:n,modelId:r,configured:i};let a=!1;try{a=w().includes(n)}catch{}return a?{providerId:n,modelId:r,configured:!1}:null}function Z(e,t,n=!0){let r=[e],i=n?Y(e.toLowerCase(),[t]):null;i!==null&&i!==e&&r.push(i);let a=[];try{a=O(t)}catch{}for(let n of r)if(!(a.length>0&&!a.some(e=>e.id===n)))try{if(K(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 it(e){return e.map(e=>`${e.providerId}:${e.modelId}`).join(`, `)}function Q(e){if(!e)return{ok:!1,error:`unknown model: (empty)`};let t=rt(e);if(t)return ct(e,t);let n=null;try{n=U()}catch{}if(n!==null){let t=Z(e,n);if(t)return st(t)}return lt(e,n)}function $(){try{return U()}catch{return null}}function at(e){let t=$();return t===null||fe(_(),t,e.providerId,e.modelId)}function ot(e){let t=$()??`<none>`;return{ok:!1,error:`model not allowed: ${`${e.providerId}:${e.modelId}`} is blocked by ${m} while "${t}" is selected — ${he(t,e.providerId,e.modelId)}`}}function st(e){return at(e)?{ok:!0,selection:e}:ot(e)}function ct(e,t){if(!t.configured){let n=X();return{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" is not configured${n.length>0?`; configured: ${n.join(`, `)}`:``})`}}let n=Z(t.modelId,t.providerId);return n?st(n):{ok:!1,error:`unknown model: ${e} (provider "${t.providerId}" does not offer "${t.modelId}")`}}function lt(e,t){let n=X().filter(e=>e!==t);for(let t of[`exact`,`shorthand`]){let r=[];for(let i of n){let n=t===`exact`?e:Y(e.toLowerCase(),[i]);if(n===null)continue;let a=Z(n,i,!1);a&&r.push(a)}let i=r.filter(at);if(i.length===0&&r.length>0)return ot(r[0]);if(i.length===1)return{ok:!0,selection:i[0]};if(i.length>1)return{ok:!1,error:`ambiguous model: ${e} matches ${it(i)} — qualify it as provider:model`}}return{ok:!1,error:`unknown model: ${e}`}}function ut(e){let t=Q(e);return t.ok?t.selection.modelId:null}function dt(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{m as _,ze as a,me as b,k as c,U as d,q as f,Le as g,Q as h,B as i,O as l,K as m,N as n,V as o,J as p,We as r,Re as s,ge as t,H as u,h as v,oe as x,le as y};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./panel-rpc-BPwAqRch.js";import{g as t,n,o as r,x as i}from"./account-store-Ds_fREWW.js";import{a}from"./diagnostics-DN_idNHx.js";import{a as o,i as s,r as c}from"./compat-Dujkqs4W.js";import{a as l,r as u,s as d,t as f}from"./legacy-api-aliases-PgIrFmtQ.js";import{i as p,n as m,r as h}from"./claude-model-version-CJiITCDG.js";import{n as g}from"./llm-session-id-BZyXP_j2.js";import{n as _,t as v}from"./silent-renew-backoff-C_Iv54ZG.js";import{getOAuthPageOrigin as ee}from"./oauth-service-CUpgufjo.js";import{n as y}from"./temperature-support-BCoY8YuX.js";import"./provider-settings-BS6vPXws.js";var te={proxyEndpoint:`https://adobe-llm-proxy.paolo-moz.workers.dev`,redirectUri:`https://www.sliccy.ai/auth/callback`,extensionRedirectUri:`https://akjjllgokmbgpbdbmafpiefnhidlmbgf.chromiumapp.org/adobe`};function ne(e,t){return m(e,t)}function b(e,t,n){return e!==`xhigh`||!t?e:p(t,n)?`xhigh`:h(t,n)?`max`:`high`}function re(e,t){let n=e?t?.thinkingLevelMap?.[e]:void 0;return b((()=>{if(typeof n==`string`)return n;switch(e){case`minimal`:case`low`:return`low`;case`medium`:return`medium`;case`xhigh`:return`xhigh`;default:return`high`}})(),t?.id,t?.name)}function ie(e,t){return async(n,r)=>{let i=t?await t(n,r)??n:n,a=i.thinking;return a&&a.type===`enabled`&&(i.thinking={type:`adaptive`,...a.display===void 0?{}:{display:a.display}},i.output_config={...i.output_config??{},effort:e}),i}}function x(e,t){if(!ne(e.id,e.name))return t;let n=t,r=b(n.effort??re(n.reasoning,e),e.id,e.name);return{...t,onPayload:ie(r,n.onPayload)}}function ae(e,t){let n={id:e.id,name:e.name??e.id},r=t?.api??e.api,i=t?.context_window??e.context_window,a=t?.max_tokens??e.max_tokens,o=t?.reasoning??e.reasoning,s=t?.input??e.input;r&&(n.api=r),i!==void 0&&(n.context_window=i),a!==void 0&&(n.max_tokens=a),o!==void 0&&(n.reasoning=o),s&&(n.input=s);let c=t?.cost??e.cost;return c&&(n.cost=c),/haiku/i.test(e.id)&&(n.compat={supportsEagerToolInputStreaming:!1}),/sonnet-5\b/i.test(e.id)&&!n.thinkingLevelMap&&(n.thinkingLevelMap={xhigh:`xhigh`}),n}const S=Object.assign({"/packages/webapp/providers/adobe-config.json":te})[`/packages/webapp/providers/adobe-config.json`]??{clientId:``,proxyEndpoint:``,scopes:`openid,profile,email`};function C(){let e=r(`adobe`);if(e)return e.replace(/\/$/,``);if(S.proxyEndpoint)return S.proxyEndpoint.replace(/\/$/,``);throw Error(`Adobe proxy endpoint not configured — set it in Settings or adobe-config.json`)}const w=new Map,T=new Map,E=`slicc-adobe-models`;function D(e){try{localStorage.setItem(E,JSON.stringify(e))}catch{}}async function O(e){let t=w.get(e);if(t)return t;try{let t=await fetch(`${e}/v1/config`,{headers:{[W]:`6.108.2`}});if(t.ok){let n=await t.json();return w.set(e,n),n}console.warn(`[adobe] Proxy /v1/config returned ${t.status}, falling back to build-time config`)}catch(e){console.warn(`[adobe] Failed to fetch proxy config:`,e instanceof Error?e.message:String(e))}return{}}function k(e){let t=e.clientId||S.clientId;if(!t)throw Error(`Could not determine IMS client ID — proxy /v1/config did not return one and adobe-config.json is empty`);return t}function A(e){return e.scopes||S.scopes}function j(e){return e.imsEnvironment||S.imsEnvironment||`prod`}const M={prod:`https://ims-na1.adobelogin.com`,stg1:`https://ims-na1-stg1.adobelogin.com`};function N(e){return M[e??S.imsEnvironment??`prod`]??M.prod}const P=typeof chrome<`u`&&!!chrome?.runtime?.id;function F(){return n().find(e=>e.providerId===`adobe`)}async function oe(e,t){try{let n=await fetch(`${N(t)}/ims/userinfo/v2`,{headers:{Authorization:`Bearer ${e}`}});if(n.ok){let e=await n.json();return{name:e.displayName||e.name||e.email,avatar:e.picture||e.avatar_url}}console.warn(`[adobe] User profile fetch returned ${n.status}, account will have no display name`)}catch(e){console.warn(`[adobe] Failed to fetch user profile:`,e instanceof Error?e.message:String(e))}return{}}function I(e){let t=e.indexOf(`#`);if(t<0)return null;let n=new URLSearchParams(e.slice(t+1)),r=n.get(`access_token`);return r?{accessToken:r,expiresIn:parseInt(n.get(`expires_in`)??`86400`,10),scope:n.get(`scope`)??void 0}:null}const L={id:`adobe`,name:`Adobe`,description:`Claude via Adobe — login with your Adobe ID`,requiresApiKey:!1,requiresBaseUrl:!S.proxyEndpoint,baseUrlPlaceholder:`https://your-proxy.example.com`,baseUrlDescription:`Anthropic-compatible proxy endpoint`,isOAuth:!0,defaultModelId:`sonnet`,oauthTokenDomains:[`ims-na1.adobelogin.com`,`ims-na1-stg1.adobelogin.com`,`*.adobelogin.com`,`*.adobe.io`,`firefall.adobe.io`,`admin.hlx.page`,`admin.hlx.live`,`admin.aem.page`,`admin.aem.live`],getModelIds:()=>{let e=e=>ae(e,T.get(e.id));for(let t of Q.values())if(t.length){let n=t.map(t=>e({id:t.id,name:t.name??t.id,reasoning:t.reasoning,input:t.input,cost:t.cost}));return D(n),n}for(let t of w.values())if(t.models?.length)return t.models.map(t=>e(t));try{let e=localStorage.getItem(E);if(e){let t=JSON.parse(e);if(t.length)return t}}catch{}return[{id:`claude-sonnet-4-6`,name:`Claude Sonnet 4.6`}]},onOAuthLogin:async(e,n,r)=>{let i=C(),a=await O(i),o=k(a),s=A(a),c=j(a),l=P?null:await ee(),u=P?null:_({pageHref:l.href,pageOrigin:l.origin,configuredRedirectUri:S.redirectUri},()=>crypto.randomUUID()),d=P?S.extensionRedirectUri??`https://${chrome.runtime.id}.chromiumapp.org/`:u.redirectUri,f=u?.oauthState,p=u?.expectedNonce??null,m=new URLSearchParams({client_id:o,scope:s,response_type:`token`,redirect_uri:d});f&&m.set(`state`,f),r?.forceReauth&&m.set(`prompt`,`login`);let h=await e(`${N(c)}/ims/authorize/v2?${m}`);if(!h)return;if(p&&h)try{if(new URL(h).searchParams.get(`nonce`)!==p){console.error(`[adobe] OAuth nonce mismatch — possible CSRF`);return}}catch(e){console.warn(`[adobe] Nonce check skipped (URL parse failed):`,e instanceof Error?e.message:String(e))}let g=I(h);if(!g){console.error(`[adobe] Could not extract token from redirect URL`);return}let v=await oe(g.accessToken,c);await t({providerId:`adobe`,accessToken:g.accessToken,tokenExpiresAt:Date.now()+g.expiresIn*1e3,userName:v.name,userAvatar:v.avatar,scopes:g.scope,baseUrl:S.proxyEndpoint?void 0:i}),await $().catch(e=>console.warn(`[adobe] Failed to fetch models after login:`,e instanceof Error?e.message:String(e))),n()},onOAuthLogout:async()=>{let e=F();if(e?.accessToken)try{let t=w.values().next().value??{},n=t.clientId||S.clientId,r=j(t);if(n){let t=await fetch(`${N(r)}/ims/revoke`,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({token:e.accessToken,token_type_hint:`access_token`,client_id:n})});t.ok||console.warn(`[adobe] Token revocation returned ${t.status}, token may still be valid server-side`)}}catch(e){console.warn(`[adobe] Failed to revoke token:`,e instanceof Error?e.message:String(e))}await t({providerId:`adobe`,accessToken:``})},onSilentRenew:async()=>F()?.accessToken?B():null,refreshModels:async e=>{await $(e);let t=L.getModelIds?.();t?.length&&D(t)}};let R=null;const se=v();async function z(){let e=F();if(!e?.accessToken)throw Error(`Not logged in to Adobe — please log in first`);if((e.tokenExpiresAt??0)-Date.now()>6e4)return e.accessToken;console.log(`[adobe] Token expired or expiring soon, attempting silent renewal...`);let t=await se.run(()=>B());if(t)return t;let n=F();if((n?.tokenExpiresAt??0)-Date.now()>0&&n?.accessToken)return n.accessToken;throw Error(`Adobe session expired — please log in again`)}function ce(){let e=F();return!e?.tokenExpiresAt||Date.now()>e.tokenExpiresAt-6e4}async function B(){if(typeof window>`u`){let t=e();if(!t)return null;try{let{accessToken:e}=await t.call(`silent-renew`,{providerId:`adobe`},{timeoutMs:13e4});return e}catch(e){return console.warn(`[adobe] worker→page silent-renew bridge failed:`,e instanceof Error?e.message:String(e)),null}}return R||(R=U().finally(()=>{R=null}),R)}function le(e,t){let n=k(t),r=A(t),i=j(t),a=P?null:_({pageHref:window.location.href,pageOrigin:window.location.origin,configuredRedirectUri:S.redirectUri},()=>crypto.randomUUID()),o=P?S.extensionRedirectUri??`https://${chrome.runtime.id}.chromiumapp.org/`:a.redirectUri,s=a?.oauthState,c=a?.expectedNonce??null,l=new URLSearchParams({client_id:n,scope:r,response_type:`token`,redirect_uri:o,prompt:`none`});return s&&l.set(`state`,s),{authorizeUrl:`${N(i)}/ims/authorize/v2?${l}`,expectedNonce:c,proxyEndpoint:e}}function V(e,t){if(!t)return!0;try{if(new URL(e).searchParams.get(`nonce`)!==t)return console.error(`[adobe] OAuth nonce mismatch — possible CSRF`),!1}catch(e){console.warn(`[adobe] Nonce check skipped (URL parse failed):`,e instanceof Error?e.message:String(e))}return!0}async function H(e,n){let r=F();await t({providerId:`adobe`,accessToken:e.accessToken,tokenExpiresAt:Date.now()+e.expiresIn*1e3,userName:r?.userName,userAvatar:r?.userAvatar,scopes:e.scope,baseUrl:S.proxyEndpoint?void 0:n})}async function U(){try{let e=C(),{authorizeUrl:t,expectedNonce:n}=le(e,await O(e)),{createOAuthLauncher:r}=await import(`./oauth-service-CUpgufjo.js`),i=await r()(t,{interactive:!1});if(!i||!V(i,n))return null;let a=I(i);return a?(await H(a,e),console.log(`[adobe] Token renewed silently`),await $().catch(e=>console.warn(`[adobe] Failed to refresh models after silent renewal:`,e instanceof Error?e.message:String(e))),a.accessToken):null}catch(e){return console.warn(`[adobe] Silent renewal error:`,e instanceof Error?e.message:String(e)),null}}const W=`X-Slicc-Version`;function G(e){let t={};if(e.headers)for(let[n,r]of Object.entries(e.headers))n.toLowerCase()!==`x-slicc-version`&&(t[n]=r);return t[W]=`6.108.2`,{...e,headers:t}}const K=new Set;function q(e,t){if(e.headers){for(let[t,n]of Object.entries(e.headers))if(t.toLowerCase()===`x-session-id`&&n!=null)return e}return K.has(t)||(K.add(t),console.warn(`[adobe] Missing X-Session-Id from ${t} — using daily fallback. Attach an X-Session-Id header at the call site (see scoop-context.ts streamWithSessionId or docs/pitfalls.md).`)),{...e,headers:{...e.headers??{},"X-Session-Id":g(`adobe-provider-fallback`)}}}function ue(){K.clear()}function J(e,t){return{type:`error`,reason:`error`,error:{role:`assistant`,content:[],api:`adobe-anthropic`,provider:`adobe`,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()}}}const Y=(e,t,n={})=>{let r=a();return(async()=>{try{let i=await z();if(String(e.api).includes(`openai`)){let a={...e,baseUrl:`${C()}/v1`,api:`openai-completions`,compat:{...e.compat,supportsStore:!1,supportsDeveloperRole:!1}},o=u(a,t,G(q({...n,apiKey:i},`streamAdobe[openai]`)));for await(let e of o)r.push(e)}else{let a={...e,baseUrl:C(),api:`anthropic-messages`},o=f(a,t,G(q(x(e,y(e.id,e.name,{...n,apiKey:i})),`streamAdobe[anthropic]`)));for await(let e of o)r.push(e)}r.end()}catch(t){console.error(`[adobe] Stream error:`,t instanceof Error?t.message:String(t)),r.push(J(e,t)),r.end()}})(),r},X=(e,t,n)=>{let r=a();return(async()=>{try{let i=await z();if(String(e.api).includes(`openai`)){let a={...e,baseUrl:`${C()}/v1`,api:`openai-completions`,compat:{...e.compat,supportsStore:!1,supportsDeveloperRole:!1}},o=d(a,t,G(q({...n,apiKey:i},`streamSimpleAdobe[openai]`)));for await(let e of o)r.push(e)}else{let a={...e,baseUrl:C(),api:`anthropic-messages`},o=l(a,t,G(q(x(e,y(e.id,e.name,{...n,apiKey:i})),`streamSimpleAdobe[anthropic]`)));for await(let e of o)r.push(e)}r.end()}catch(t){console.error(`[adobe] Stream error:`,t instanceof Error?t.message:String(t)),r.push(J(e,t)),r.end()}})(),r};function Z(e){let t={id:e.id,name:e.name};return e.api!==void 0&&(t.api=e.api),e.context_window!==void 0&&(t.context_window=e.context_window),e.max_tokens!==void 0&&(t.max_tokens=e.max_tokens),e.reasoning!==void 0&&(t.reasoning=e.reasoning),e.input!==void 0&&(t.input=e.input),t}function de(){let e=new Map;for(let t of s())try{for(let n of c(t))e.set(n.id,n)}catch{}return e}function fe(e,t,n){let r=`adobe-${e.api===`openai`?`openai`:`anthropic`}`,a=n.get(e.id);if(a)return{...a,provider:`adobe`,api:r};let o=i(e.id,n);return{id:e.id,name:e.name??e.id,provider:`adobe`,api:r,baseUrl:t,contextWindow:2e5,maxTokens:16384,input:[`text`,`image`],cost:o,inputCost:o.input,outputCost:o.output,cacheReadCost:o.cacheRead,cacheWriteCost:o.cacheWrite,reasoning:!0}}function pe(e,t){for(let t of e)T.set(t.id,Z(t));let n=de();return e.map(e=>fe(e,t,n))}async function me(e){try{let t=e??await z(),n=C(),r=await fetch(`${n}/v1/models`,{headers:{Authorization:`Bearer ${t}`,[W]:`6.108.2`}});if(!r.ok)console.warn(`[adobe] Proxy /v1/models returned ${r.status}, falling back to Anthropic models`);else{let e=await r.json();if(e.data?.length)return pe(e.data,n)}}catch(e){console.warn(`[adobe] Failed to fetch proxy models:`,e instanceof Error?e.message:String(e))}return null}const Q=new Map;function he(){return c(`anthropic`).map(e=>({...e,provider:`adobe`,api:`adobe-anthropic`}))}async function $(e){let t=C(),n=Q.get(t);if(n)return n;let r=await me(e);return r?(Q.set(t,r),r):he()}function ge(){o({api:`adobe-anthropic`,stream:Y,streamSimple:X}),o({api:`adobe-openai`,stream:Y,streamSimple:X})}export{ue as __resetAdobeSessionIdWarningCacheForTests,L as config,I as extractTokenFromUrl,$ as getAdobeModels,z as getValidAccessToken,ce as isTokenExpired,ge as register};
|
|
1
|
+
import{n as e}from"./panel-rpc-BPwAqRch.js";import{g as t,n,o as r,x as i}from"./account-store-DmTUzOwD.js";import{a}from"./diagnostics-DN_idNHx.js";import{a as o,i as s,r as c}from"./compat-Dujkqs4W.js";import{a as l,r as u,s as d,t as f}from"./legacy-api-aliases-PgIrFmtQ.js";import{i as p,n as m,r as h}from"./claude-model-version-CJiITCDG.js";import{n as g}from"./llm-session-id-BZyXP_j2.js";import{n as _,t as v}from"./silent-renew-backoff-C_Iv54ZG.js";import{getOAuthPageOrigin as ee}from"./oauth-service-CUpgufjo.js";import{n as y}from"./temperature-support-BCoY8YuX.js";import"./provider-settings-BbKZBkmj.js";var te={proxyEndpoint:`https://adobe-llm-proxy.paolo-moz.workers.dev`,redirectUri:`https://www.sliccy.ai/auth/callback`,extensionRedirectUri:`https://akjjllgokmbgpbdbmafpiefnhidlmbgf.chromiumapp.org/adobe`};function ne(e,t){return m(e,t)}function b(e,t,n){return e!==`xhigh`||!t?e:p(t,n)?`xhigh`:h(t,n)?`max`:`high`}function re(e,t){let n=e?t?.thinkingLevelMap?.[e]:void 0;return b((()=>{if(typeof n==`string`)return n;switch(e){case`minimal`:case`low`:return`low`;case`medium`:return`medium`;case`xhigh`:return`xhigh`;default:return`high`}})(),t?.id,t?.name)}function ie(e,t){return async(n,r)=>{let i=t?await t(n,r)??n:n,a=i.thinking;return a&&a.type===`enabled`&&(i.thinking={type:`adaptive`,...a.display===void 0?{}:{display:a.display}},i.output_config={...i.output_config??{},effort:e}),i}}function x(e,t){if(!ne(e.id,e.name))return t;let n=t,r=b(n.effort??re(n.reasoning,e),e.id,e.name);return{...t,onPayload:ie(r,n.onPayload)}}function ae(e,t){let n={id:e.id,name:e.name??e.id},r=t?.api??e.api,i=t?.context_window??e.context_window,a=t?.max_tokens??e.max_tokens,o=t?.reasoning??e.reasoning,s=t?.input??e.input;r&&(n.api=r),i!==void 0&&(n.context_window=i),a!==void 0&&(n.max_tokens=a),o!==void 0&&(n.reasoning=o),s&&(n.input=s);let c=t?.cost??e.cost;return c&&(n.cost=c),/haiku/i.test(e.id)&&(n.compat={supportsEagerToolInputStreaming:!1}),/sonnet-5\b/i.test(e.id)&&!n.thinkingLevelMap&&(n.thinkingLevelMap={xhigh:`xhigh`}),n}const S=Object.assign({"/packages/webapp/providers/adobe-config.json":te})[`/packages/webapp/providers/adobe-config.json`]??{clientId:``,proxyEndpoint:``,scopes:`openid,profile,email`};function C(){let e=r(`adobe`);if(e)return e.replace(/\/$/,``);if(S.proxyEndpoint)return S.proxyEndpoint.replace(/\/$/,``);throw Error(`Adobe proxy endpoint not configured — set it in Settings or adobe-config.json`)}const w=new Map,T=new Map,E=`slicc-adobe-models`;function D(e){try{localStorage.setItem(E,JSON.stringify(e))}catch{}}async function O(e){let t=w.get(e);if(t)return t;try{let t=await fetch(`${e}/v1/config`,{headers:{[W]:`6.110.0`}});if(t.ok){let n=await t.json();return w.set(e,n),n}console.warn(`[adobe] Proxy /v1/config returned ${t.status}, falling back to build-time config`)}catch(e){console.warn(`[adobe] Failed to fetch proxy config:`,e instanceof Error?e.message:String(e))}return{}}function k(e){let t=e.clientId||S.clientId;if(!t)throw Error(`Could not determine IMS client ID — proxy /v1/config did not return one and adobe-config.json is empty`);return t}function A(e){return e.scopes||S.scopes}function j(e){return e.imsEnvironment||S.imsEnvironment||`prod`}const M={prod:`https://ims-na1.adobelogin.com`,stg1:`https://ims-na1-stg1.adobelogin.com`};function N(e){return M[e??S.imsEnvironment??`prod`]??M.prod}const P=typeof chrome<`u`&&!!chrome?.runtime?.id;function F(){return n().find(e=>e.providerId===`adobe`)}async function oe(e,t){try{let n=await fetch(`${N(t)}/ims/userinfo/v2`,{headers:{Authorization:`Bearer ${e}`}});if(n.ok){let e=await n.json();return{name:e.displayName||e.name||e.email,avatar:e.picture||e.avatar_url}}console.warn(`[adobe] User profile fetch returned ${n.status}, account will have no display name`)}catch(e){console.warn(`[adobe] Failed to fetch user profile:`,e instanceof Error?e.message:String(e))}return{}}function I(e){let t=e.indexOf(`#`);if(t<0)return null;let n=new URLSearchParams(e.slice(t+1)),r=n.get(`access_token`);return r?{accessToken:r,expiresIn:parseInt(n.get(`expires_in`)??`86400`,10),scope:n.get(`scope`)??void 0}:null}const L={id:`adobe`,name:`Adobe`,description:`Claude via Adobe — login with your Adobe ID`,requiresApiKey:!1,requiresBaseUrl:!S.proxyEndpoint,baseUrlPlaceholder:`https://your-proxy.example.com`,baseUrlDescription:`Anthropic-compatible proxy endpoint`,isOAuth:!0,defaultModelId:`sonnet`,oauthTokenDomains:[`ims-na1.adobelogin.com`,`ims-na1-stg1.adobelogin.com`,`*.adobelogin.com`,`*.adobe.io`,`firefall.adobe.io`,`admin.hlx.page`,`admin.hlx.live`,`admin.aem.page`,`admin.aem.live`],getModelIds:()=>{let e=e=>ae(e,T.get(e.id));for(let t of Q.values())if(t.length){let n=t.map(t=>e({id:t.id,name:t.name??t.id,reasoning:t.reasoning,input:t.input,cost:t.cost}));return D(n),n}for(let t of w.values())if(t.models?.length)return t.models.map(t=>e(t));try{let e=localStorage.getItem(E);if(e){let t=JSON.parse(e);if(t.length)return t}}catch{}return[{id:`claude-sonnet-4-6`,name:`Claude Sonnet 4.6`}]},onOAuthLogin:async(e,n,r)=>{let i=C(),a=await O(i),o=k(a),s=A(a),c=j(a),l=P?null:await ee(),u=P?null:_({pageHref:l.href,pageOrigin:l.origin,configuredRedirectUri:S.redirectUri},()=>crypto.randomUUID()),d=P?S.extensionRedirectUri??`https://${chrome.runtime.id}.chromiumapp.org/`:u.redirectUri,f=u?.oauthState,p=u?.expectedNonce??null,m=new URLSearchParams({client_id:o,scope:s,response_type:`token`,redirect_uri:d});f&&m.set(`state`,f),r?.forceReauth&&m.set(`prompt`,`login`);let h=await e(`${N(c)}/ims/authorize/v2?${m}`);if(!h)return;if(p&&h)try{if(new URL(h).searchParams.get(`nonce`)!==p){console.error(`[adobe] OAuth nonce mismatch — possible CSRF`);return}}catch(e){console.warn(`[adobe] Nonce check skipped (URL parse failed):`,e instanceof Error?e.message:String(e))}let g=I(h);if(!g){console.error(`[adobe] Could not extract token from redirect URL`);return}let v=await oe(g.accessToken,c);await t({providerId:`adobe`,accessToken:g.accessToken,tokenExpiresAt:Date.now()+g.expiresIn*1e3,userName:v.name,userAvatar:v.avatar,scopes:g.scope,baseUrl:S.proxyEndpoint?void 0:i}),await $().catch(e=>console.warn(`[adobe] Failed to fetch models after login:`,e instanceof Error?e.message:String(e))),n()},onOAuthLogout:async()=>{let e=F();if(e?.accessToken)try{let t=w.values().next().value??{},n=t.clientId||S.clientId,r=j(t);if(n){let t=await fetch(`${N(r)}/ims/revoke`,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({token:e.accessToken,token_type_hint:`access_token`,client_id:n})});t.ok||console.warn(`[adobe] Token revocation returned ${t.status}, token may still be valid server-side`)}}catch(e){console.warn(`[adobe] Failed to revoke token:`,e instanceof Error?e.message:String(e))}await t({providerId:`adobe`,accessToken:``})},onSilentRenew:async()=>F()?.accessToken?B():null,refreshModels:async e=>{await $(e);let t=L.getModelIds?.();t?.length&&D(t)}};let R=null;const se=v();async function z(){let e=F();if(!e?.accessToken)throw Error(`Not logged in to Adobe — please log in first`);if((e.tokenExpiresAt??0)-Date.now()>6e4)return e.accessToken;console.log(`[adobe] Token expired or expiring soon, attempting silent renewal...`);let t=await se.run(()=>B());if(t)return t;let n=F();if((n?.tokenExpiresAt??0)-Date.now()>0&&n?.accessToken)return n.accessToken;throw Error(`Adobe session expired — please log in again`)}function ce(){let e=F();return!e?.tokenExpiresAt||Date.now()>e.tokenExpiresAt-6e4}async function B(){if(typeof window>`u`){let t=e();if(!t)return null;try{let{accessToken:e}=await t.call(`silent-renew`,{providerId:`adobe`},{timeoutMs:13e4});return e}catch(e){return console.warn(`[adobe] worker→page silent-renew bridge failed:`,e instanceof Error?e.message:String(e)),null}}return R||(R=U().finally(()=>{R=null}),R)}function le(e,t){let n=k(t),r=A(t),i=j(t),a=P?null:_({pageHref:window.location.href,pageOrigin:window.location.origin,configuredRedirectUri:S.redirectUri},()=>crypto.randomUUID()),o=P?S.extensionRedirectUri??`https://${chrome.runtime.id}.chromiumapp.org/`:a.redirectUri,s=a?.oauthState,c=a?.expectedNonce??null,l=new URLSearchParams({client_id:n,scope:r,response_type:`token`,redirect_uri:o,prompt:`none`});return s&&l.set(`state`,s),{authorizeUrl:`${N(i)}/ims/authorize/v2?${l}`,expectedNonce:c,proxyEndpoint:e}}function V(e,t){if(!t)return!0;try{if(new URL(e).searchParams.get(`nonce`)!==t)return console.error(`[adobe] OAuth nonce mismatch — possible CSRF`),!1}catch(e){console.warn(`[adobe] Nonce check skipped (URL parse failed):`,e instanceof Error?e.message:String(e))}return!0}async function H(e,n){let r=F();await t({providerId:`adobe`,accessToken:e.accessToken,tokenExpiresAt:Date.now()+e.expiresIn*1e3,userName:r?.userName,userAvatar:r?.userAvatar,scopes:e.scope,baseUrl:S.proxyEndpoint?void 0:n})}async function U(){try{let e=C(),{authorizeUrl:t,expectedNonce:n}=le(e,await O(e)),{createOAuthLauncher:r}=await import(`./oauth-service-CUpgufjo.js`),i=await r()(t,{interactive:!1});if(!i||!V(i,n))return null;let a=I(i);return a?(await H(a,e),console.log(`[adobe] Token renewed silently`),await $().catch(e=>console.warn(`[adobe] Failed to refresh models after silent renewal:`,e instanceof Error?e.message:String(e))),a.accessToken):null}catch(e){return console.warn(`[adobe] Silent renewal error:`,e instanceof Error?e.message:String(e)),null}}const W=`X-Slicc-Version`;function G(e){let t={};if(e.headers)for(let[n,r]of Object.entries(e.headers))n.toLowerCase()!==`x-slicc-version`&&(t[n]=r);return t[W]=`6.110.0`,{...e,headers:t}}const K=new Set;function q(e,t){if(e.headers){for(let[t,n]of Object.entries(e.headers))if(t.toLowerCase()===`x-session-id`&&n!=null)return e}return K.has(t)||(K.add(t),console.warn(`[adobe] Missing X-Session-Id from ${t} — using daily fallback. Attach an X-Session-Id header at the call site (see scoop-context.ts streamWithSessionId or docs/pitfalls.md).`)),{...e,headers:{...e.headers??{},"X-Session-Id":g(`adobe-provider-fallback`)}}}function ue(){K.clear()}function J(e,t){return{type:`error`,reason:`error`,error:{role:`assistant`,content:[],api:`adobe-anthropic`,provider:`adobe`,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()}}}const Y=(e,t,n={})=>{let r=a();return(async()=>{try{let i=await z();if(String(e.api).includes(`openai`)){let a={...e,baseUrl:`${C()}/v1`,api:`openai-completions`,compat:{...e.compat,supportsStore:!1,supportsDeveloperRole:!1}},o=u(a,t,G(q({...n,apiKey:i},`streamAdobe[openai]`)));for await(let e of o)r.push(e)}else{let a={...e,baseUrl:C(),api:`anthropic-messages`},o=f(a,t,G(q(x(e,y(e.id,e.name,{...n,apiKey:i})),`streamAdobe[anthropic]`)));for await(let e of o)r.push(e)}r.end()}catch(t){console.error(`[adobe] Stream error:`,t instanceof Error?t.message:String(t)),r.push(J(e,t)),r.end()}})(),r},X=(e,t,n)=>{let r=a();return(async()=>{try{let i=await z();if(String(e.api).includes(`openai`)){let a={...e,baseUrl:`${C()}/v1`,api:`openai-completions`,compat:{...e.compat,supportsStore:!1,supportsDeveloperRole:!1}},o=d(a,t,G(q({...n,apiKey:i},`streamSimpleAdobe[openai]`)));for await(let e of o)r.push(e)}else{let a={...e,baseUrl:C(),api:`anthropic-messages`},o=l(a,t,G(q(x(e,y(e.id,e.name,{...n,apiKey:i})),`streamSimpleAdobe[anthropic]`)));for await(let e of o)r.push(e)}r.end()}catch(t){console.error(`[adobe] Stream error:`,t instanceof Error?t.message:String(t)),r.push(J(e,t)),r.end()}})(),r};function Z(e){let t={id:e.id,name:e.name};return e.api!==void 0&&(t.api=e.api),e.context_window!==void 0&&(t.context_window=e.context_window),e.max_tokens!==void 0&&(t.max_tokens=e.max_tokens),e.reasoning!==void 0&&(t.reasoning=e.reasoning),e.input!==void 0&&(t.input=e.input),t}function de(){let e=new Map;for(let t of s())try{for(let n of c(t))e.set(n.id,n)}catch{}return e}function fe(e,t,n){let r=`adobe-${e.api===`openai`?`openai`:`anthropic`}`,a=n.get(e.id);if(a)return{...a,provider:`adobe`,api:r};let o=i(e.id,n);return{id:e.id,name:e.name??e.id,provider:`adobe`,api:r,baseUrl:t,contextWindow:2e5,maxTokens:16384,input:[`text`,`image`],cost:o,inputCost:o.input,outputCost:o.output,cacheReadCost:o.cacheRead,cacheWriteCost:o.cacheWrite,reasoning:!0}}function pe(e,t){for(let t of e)T.set(t.id,Z(t));let n=de();return e.map(e=>fe(e,t,n))}async function me(e){try{let t=e??await z(),n=C(),r=await fetch(`${n}/v1/models`,{headers:{Authorization:`Bearer ${t}`,[W]:`6.110.0`}});if(!r.ok)console.warn(`[adobe] Proxy /v1/models returned ${r.status}, falling back to Anthropic models`);else{let e=await r.json();if(e.data?.length)return pe(e.data,n)}}catch(e){console.warn(`[adobe] Failed to fetch proxy models:`,e instanceof Error?e.message:String(e))}return null}const Q=new Map;function he(){return c(`anthropic`).map(e=>({...e,provider:`adobe`,api:`adobe-anthropic`}))}async function $(e){let t=C(),n=Q.get(t);if(n)return n;let r=await me(e);return r?(Q.set(t,r),r):he()}function ge(){o({api:`adobe-anthropic`,stream:Y,streamSimple:X}),o({api:`adobe-openai`,stream:Y,streamSimple:X})}export{ue as __resetAdobeSessionIdWarningCacheForTests,L as config,I as extractTokenFromUrl,$ as getAdobeModels,z as getValidAccessToken,ce as isTokenExpired,ge as register};
|