sliccy 6.103.1 → 6.104.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.
Files changed (153) hide show
  1. package/README.md +2 -0
  2. package/dist/node-server/_shared/base64.d.ts +20 -4
  3. package/dist/node-server/_shared/base64.d.ts.map +1 -1
  4. package/dist/node-server/_shared/base64.js +54 -16
  5. package/dist/node-server/_shared/base64.js.map +1 -1
  6. package/dist/ui/.vite/manifest.json +244 -244
  7. package/dist/ui/assets/{account-store-jE2BN3Yk.js → account-store-BmJKIPqf.js} +3 -3
  8. package/dist/ui/assets/{account-store-DBDG3eY5.js → account-store-IiqFvQxF.js} +2 -2
  9. package/dist/ui/assets/{adobe-Cr3-KUNL.js → adobe-ENkhvelX.js} +2 -2
  10. package/dist/ui/assets/{adobe-BtWe6ynz.js → adobe-V7ngPPFB.js} +1 -1
  11. package/dist/ui/assets/{agent-message-to-chat-LqFvUGRI.js → agent-message-to-chat-CjWIKGH-.js} +1 -1
  12. package/dist/ui/assets/{agent-panels-xdzXMPvs.js → agent-panels-D8HPvbSm.js} +1 -1
  13. package/dist/ui/assets/{apply-layout-DLmb1Bhe.js → apply-layout-CT5ajQg2.js} +1 -1
  14. package/dist/ui/assets/{apps-CiSp4gGP.js → apps-C2-yNti0.js} +1 -1
  15. package/dist/ui/assets/{azure-openai-pxsoq7cS.js → azure-openai-D6U381kS.js} +1 -1
  16. package/dist/ui/assets/{azure-openai-CqUcCQVL.js → azure-openai-dxPSwxHX.js} +1 -1
  17. package/dist/ui/assets/base64-DJRlyqZu.js +1 -0
  18. package/dist/ui/assets/base64-DzRgusa4.js +1 -0
  19. package/dist/ui/assets/{cerebras-DxPNyNmZ.js → cerebras-CJG0lmgZ.js} +1 -1
  20. package/dist/ui/assets/{cerebras-B23nlLot.js → cerebras-DMAP0cNO.js} +1 -1
  21. package/dist/ui/assets/{client-DVC0x03n.js → client-Beg9O208.js} +1 -1
  22. package/dist/ui/assets/{composer-speech-Byuwd2QJ.js → composer-speech-C08Pruqj.js} +1 -1
  23. package/dist/ui/assets/{connect-surface-DGNbk04y.js → connect-surface-DmxJIbHQ.js} +1 -1
  24. package/dist/ui/assets/{derive-Do9C10kj.js → derive-D0xHQ8be.js} +1 -1
  25. package/dist/ui/assets/{discovery-preference-__0HPYJC.js → discovery-preference-cJij_YcT.js} +1 -1
  26. package/dist/ui/assets/{ensure-speech-assets-Hv5Jwof_.js → ensure-speech-assets-CmarhXRU.js} +1 -1
  27. package/dist/ui/assets/{float-topology-DiK91BlA.js → float-topology-BYb7rxY5.js} +1 -1
  28. package/dist/ui/assets/{follower-navigate-watcher-DRFF6ll0.js → follower-navigate-watcher-BoAzVfrf.js} +1 -1
  29. package/dist/ui/assets/{fs-DcL35kJN.js → fs-jddXs9y2.js} +2 -2
  30. package/dist/ui/assets/{fs-CfMjuZHe.js → fs-m1HwXJLG.js} +1 -1
  31. package/dist/ui/assets/{github-BaCTkxuT.js → github-BljExxxl.js} +1 -1
  32. package/dist/ui/assets/{github-DtNqprWS.js → github-Do8ICJHI.js} +2 -2
  33. package/dist/ui/assets/{github-copilot-DBwOokVd.js → github-copilot-B1tRU69x.js} +1 -1
  34. package/dist/ui/assets/{github-copilot-DL4stlfl.js → github-copilot-D9oFUdvW.js} +1 -1
  35. package/dist/ui/assets/{github-install-B0noPq8m.js → github-install-CUvUxsIv.js} +1 -1
  36. package/dist/ui/assets/{github-zip-CH5oj-va.js → github-zip-Bs6pXdFH.js} +1 -1
  37. package/dist/ui/assets/{hear-zfLnACrB.js → hear-Du8SABf8.js} +1 -1
  38. package/dist/ui/assets/image-markers-CxJ9ZBgw.js +1 -0
  39. package/dist/ui/assets/image-markers-D3wDAnBQ.js +1 -0
  40. package/dist/ui/assets/{js-realm-worker-ChRV4jfs.js → js-realm-worker-DSes0sDh.js} +1 -1
  41. package/dist/ui/assets/{kernel-worker-wISI56TC.js → kernel-worker-B28TODxd.js} +598 -604
  42. package/dist/ui/assets/{lick-urls-DcXOqXGm.js → lick-urls-51I5JBZv.js} +1 -1
  43. package/dist/ui/assets/{lick-ws-bridge-CnkYvHKI.js → lick-ws-bridge-BJTVP6Z3.js} +1 -1
  44. package/dist/ui/assets/{lick-ws-bridge-CdCpPLQn.js → lick-ws-bridge-rxCk9AYw.js} +1 -1
  45. package/dist/ui/assets/{local-llm-CgyqO7JD.js → local-llm-BpWVG7rb.js} +1 -1
  46. package/dist/ui/assets/{local-llm-DCZf27Ic.js → local-llm-DOlE_mq0.js} +1 -1
  47. package/dist/ui/assets/{main-hZb8JvUt.js → main-BtqcEAf5.js} +3 -3
  48. package/dist/ui/assets/{mime-types-ugOWOUZ9.js → mime-types-k2E6ulyv.js} +1 -1
  49. package/dist/ui/assets/mount-BkpFTojV.js +1 -0
  50. package/dist/ui/assets/mount-Dd5dDOL5.js +2 -0
  51. package/dist/ui/assets/{mount-bridge-client-DZE6sHdr.js → mount-bridge-client-BJeJ5p60.js} +1 -1
  52. package/dist/ui/assets/{mount-recovery-BYDMyV5x.js → mount-recovery-Guc6Q7BU.js} +1 -1
  53. package/dist/ui/assets/{new-session-DZt_Pkmi.js → new-session-BoQLo0jr.js} +2 -2
  54. package/dist/ui/assets/{oauth-bootstrap-C4f3Pqb4.js → oauth-bootstrap-Bn6ni8m6.js} +2 -2
  55. package/dist/ui/assets/{oauth-service-CTntqv0R.js → oauth-service-CUpgufjo.js} +1 -1
  56. package/dist/ui/assets/{oauth-service-Uk8UIb05.js → oauth-service-DJuzPXsg.js} +1 -1
  57. package/dist/ui/assets/{onboarding-orchestrator-DAaCDuF7.js → onboarding-orchestrator-U8D_WgM3.js} +1 -1
  58. package/dist/ui/assets/{openai-codex-jW889ehY.js → openai-codex-BEG15TVt.js} +1 -1
  59. package/dist/ui/assets/{openai-codex-CovW8uCy.js → openai-codex-D0bUPtBU.js} +1 -1
  60. package/dist/ui/assets/{openrouter-BbvI6brK.js → openrouter-Cb6vTZpw.js} +1 -1
  61. package/dist/ui/assets/{openrouter-BEH4oZQ8.js → openrouter-Crh8YgQ9.js} +1 -1
  62. package/dist/ui/assets/{openrouter-oauth-C7DAIiEs.js → openrouter-oauth-Ae456v76.js} +1 -1
  63. package/dist/ui/assets/{openrouter-oauth-d-g3LZa9.js → openrouter-oauth-BJnOa1H0.js} +1 -1
  64. package/dist/ui/assets/{page-approval-service-BsXoPdFJ.js → page-approval-service-Z5cL8IlA.js} +1 -1
  65. package/dist/ui/assets/{panel-rpc-handlers-Dl7Wkwha.js → panel-rpc-handlers-BIQygnJj.js} +2 -2
  66. package/dist/ui/assets/{panelize-shell-IXuv4hrf.js → panelize-shell-B69-apOi.js} +3 -3
  67. package/dist/ui/assets/{port-bridge-client-BhmNRlRv.js → port-bridge-client-Cti-DmEY.js} +1 -1
  68. package/dist/ui/assets/{port-bridge-client-Bz2N1tvn.js → port-bridge-client-D5ndzGCE.js} +1 -1
  69. package/dist/ui/assets/{provider-qK_F6dXG.js → provider-B77vujtm.js} +2 -2
  70. package/dist/ui/assets/{provider-OpXYNXxw.js → provider-BTCt1a5M.js} +1 -1
  71. package/dist/ui/assets/{provider-settings-8TciU1FW.js → provider-settings-CjRIHeBL.js} +1 -1
  72. package/dist/ui/assets/provider-store-access-BJuEQWzj.js +1 -0
  73. package/dist/ui/assets/provider-store-access-DwO-dyKc.js +1 -0
  74. package/dist/ui/assets/{providers-1Yt5F_Gw.js → providers-DSoO_C6K.js} +1 -1
  75. package/dist/ui/assets/{providers-B0Ow1wre.js → providers-FS2J0XfF.js} +2 -2
  76. package/dist/ui/assets/proxied-fetch-BduXImRw.js +1 -0
  77. package/dist/ui/assets/proxied-fetch-CFWkfPNA.js +2 -0
  78. package/dist/ui/assets/{quick-llm-XPUIFhcf.js → quick-llm-BkiH_6dY.js} +1 -1
  79. package/dist/ui/assets/{recovery-screen-k4xISnFw.js → recovery-screen-D1QyEOdx.js} +1 -1
  80. package/dist/ui/assets/{redirect-uri-BMW_ipVV.js → redirect-uri-DlDnQlQH.js} +1 -1
  81. package/dist/ui/assets/run-8gbia05d.js +81 -0
  82. package/dist/ui/assets/{secret-env-CjwX7z7b.js → secret-env-Hwyj0iR1.js} +1 -1
  83. package/dist/ui/assets/secret-topology-BE7Eyd3P.js +1 -0
  84. package/dist/ui/assets/{secrets-bridge-client-DCQ44RyR.js → secrets-bridge-client-CHZomqvU.js} +1 -1
  85. package/dist/ui/assets/{setup-feature-flags-remote-DgMAcz5y.js → setup-feature-flags-remote-KiV_vpC_.js} +1 -1
  86. package/dist/ui/assets/shared-2T6O01rS.js +1 -0
  87. package/dist/ui/assets/sidecar-repair-Bc7f-qAI.js +1 -0
  88. package/dist/ui/assets/{skills-BMQFEo-W.js → skills-BSLkuDhj.js} +1 -1
  89. package/dist/ui/assets/slicc-version-CffU1Fgw.js +1 -0
  90. package/dist/ui/assets/{speak-BsVaFA0Z.js → speak-DW89OFqf.js} +1 -1
  91. package/dist/ui/assets/{sprinkle-bridge-e00Na2NA.js → sprinkle-bridge-D4YmJlZ6.js} +1 -1
  92. package/dist/ui/assets/{sprinkle-manager-BqdBktzt.js → sprinkle-manager-DAE09Bc2.js} +1 -1
  93. package/dist/ui/assets/state-wjrFrzRJ.js +42 -0
  94. package/dist/ui/assets/{store-CjnUQeS5.js → store-C_vXBvA7.js} +1 -1
  95. package/dist/ui/assets/{store-C0sefGeA.js → store-DMAUhVMh.js} +1 -1
  96. package/dist/ui/assets/{sudo-D64f8-DS.js → sudo-uUM_PEW2.js} +1 -1
  97. package/dist/ui/assets/{sync-dialog-DVOHDT_N.js → sync-dialog-B1Zaj4wJ.js} +1 -1
  98. package/dist/ui/assets/{transformers-env-_JNZLFlo.js → transformers-env-plTpP5Zy.js} +1 -1
  99. package/dist/ui/assets/{tray-leader-Mq6HIneb.js → tray-leader-B_FxjW0d.js} +1 -1
  100. package/dist/ui/assets/{tray-leave-runtime-CN7V9V7O.js → tray-leave-runtime-3_wlFPFU.js} +1 -1
  101. package/dist/ui/assets/{tray-runtime-config-D7cmCcHJ.js → tray-runtime-config-Bpo7AZhY.js} +1 -1
  102. package/dist/ui/assets/{tray-runtime-config-CVoPQrT2.js → tray-runtime-config-Dv3NVv5v.js} +1 -1
  103. package/dist/ui/assets/{upgrade-detection-DWUWwgu0.js → upgrade-detection-BZydnliA.js} +1 -1
  104. package/dist/ui/assets/{voice-reply-taVlSKij.js → voice-reply-CX6bFYcn.js} +1 -1
  105. package/dist/ui/assets/wc-attach-BkTbG_ly.js +13 -0
  106. package/dist/ui/assets/{wc-extension-CIcRpM56.js → wc-extension-E7Frt2oB.js} +2 -2
  107. package/dist/ui/assets/wc-float-label-BFAB8N7T.js +1 -0
  108. package/dist/ui/assets/{wc-floatbar-online-Blu7uPcz.js → wc-floatbar-online-8LtSd2qH.js} +1 -1
  109. package/dist/ui/assets/wc-follower-CiI-JkaB.js +17 -0
  110. package/dist/ui/assets/wc-follower-oauth-DFGxpDk0.js +5 -0
  111. package/dist/ui/assets/wc-live-D2AOU6cb.js +399 -0
  112. package/dist/ui/assets/wc-nav-DsTH8Xrh.js +2 -0
  113. package/dist/ui/assets/{wc-onboarding-BKOrQndt.js → wc-onboarding-Df_du4AO.js} +2 -2
  114. package/dist/ui/assets/{wc-placeholder-HxaZhqjK.js → wc-placeholder-DGci87zI.js} +3 -3
  115. package/dist/ui/assets/{wc-settings-B-lBzH3m.js → wc-settings-BOiDVH-h.js} +3 -3
  116. package/dist/ui/assets/{wc-shell-C5878kp7.js → wc-shell-BNnCLD-w.js} +234 -192
  117. package/dist/ui/assets/{wc-shortcut-config-D-jCGQf8.js → wc-shortcut-config-Dc4NhmIf.js} +2 -2
  118. package/dist/ui/assets/{wc-sprinkles-DRG4NrmV.js → wc-sprinkles-BD2yq34b.js} +2 -2
  119. package/dist/ui/assets/wc-transcript-export-BD-MB_QD.js +2 -0
  120. package/dist/ui/assets/wc-tray-B5valYx5.js +2 -0
  121. package/dist/ui/assets/welcome-detection-B8IanYtP.js +1 -0
  122. package/dist/ui/assets/{whisper-session-Ba-J0yyJ.js → whisper-session-B1agEx3i.js} +2 -2
  123. package/dist/ui/assets/{xai-grok-DfiTAalZ.js → xai-grok-CB2eT78f.js} +1 -1
  124. package/dist/ui/assets/{xai-grok-A8MC2Iaf.js → xai-grok-hlQX59AH.js} +1 -1
  125. package/dist/ui/index.html +11 -11
  126. package/dist/ui/llm-proxy-sw.js +1 -1
  127. package/dist/ui/packages/webapp/index.html +11 -11
  128. package/package.json +6 -5
  129. package/dist/ui/assets/attachments-CByeG2Ru.js +0 -1
  130. package/dist/ui/assets/base64-DpPqYZJF.js +0 -1
  131. package/dist/ui/assets/image-markers-BXtVm4og.js +0 -1
  132. package/dist/ui/assets/image-markers-CdcIBWOf.js +0 -1
  133. package/dist/ui/assets/mount-ClDr48Im.js +0 -2
  134. package/dist/ui/assets/mount-Ct_GLYI9.js +0 -1
  135. package/dist/ui/assets/provider-store-access-DLbJ_2-7.js +0 -1
  136. package/dist/ui/assets/provider-store-access-Dmn0ODjH.js +0 -1
  137. package/dist/ui/assets/proxied-fetch-BGeNrvQ4.js +0 -2
  138. package/dist/ui/assets/proxied-fetch-Co-pb0x0.js +0 -1
  139. package/dist/ui/assets/run-DIBEBHmW.js +0 -81
  140. package/dist/ui/assets/secret-topology-0AWfFTh5.js +0 -1
  141. package/dist/ui/assets/shared-B9N4MZd0.js +0 -1
  142. package/dist/ui/assets/sidecar-repair-ASAHpXI1.js +0 -1
  143. package/dist/ui/assets/slicc-version-Ck2OIDLw.js +0 -1
  144. package/dist/ui/assets/state-ByRTuiZz.js +0 -42
  145. package/dist/ui/assets/wc-attach-Dq5TXpBJ.js +0 -13
  146. package/dist/ui/assets/wc-float-label-EMnMWch_.js +0 -1
  147. package/dist/ui/assets/wc-follower-DdOa7zTC.js +0 -17
  148. package/dist/ui/assets/wc-follower-oauth--MzGpcmZ.js +0 -5
  149. package/dist/ui/assets/wc-live-D4uCvaCk.js +0 -399
  150. package/dist/ui/assets/wc-nav-oBBPokXv.js +0 -2
  151. package/dist/ui/assets/wc-transcript-export-6lmkA73A.js +0 -2
  152. package/dist/ui/assets/wc-tray-XB4WIW8D.js +0 -2
  153. package/dist/ui/assets/welcome-detection-oFQzS8aL.js +0 -1
package/README.md CHANGED
@@ -34,6 +34,7 @@ Once you're in, you can:
34
34
  - Connect other browser windows or Electron apps into one shared session
35
35
  - Run leader-shell commands from the native iOS follower's Terminal tab, or ask the leader to open another iOS app after an on-device approval
36
36
  - Approve the agent's sudo requests from your iPhone with Face ID — including when the leader is running headless in the cloud — and get a push when a turn finishes or an approval is waiting
37
+ - Keep an eye on the session from a **Cones & Scoops** home-screen widget on iPhone, iPad and the Mac desktop: what the cone is doing, the scoops under it, how full each context is, and whether anything needs you ([details](docs/widgets.md))
37
38
  - Install skills that teach the agent how to perform challenging tasks
38
39
  - Give it practical tools models already know how to use (`bash`, `git`, `node`, `python`, `playwright`)
39
40
  - Delegate parallel work to sub-agents so tasks get done faster
@@ -70,6 +71,7 @@ SLICC is for you if:
70
71
  - **Serve driveable previews.** `serve --bridge <dir>` mints a URL whose every visitor tab becomes a live, leader-driveable synthetic-CDP target — the agent can navigate/click/evaluate/screenshot and weave in webhook licks. **Security: opt-in only; cross-subdomain cookie risk accepted and documented (host-only cookies isolated per preview; `Domain=.sliccy.now` cookies readable across all previews).**
71
72
  - **Publish persistent previews.** `serve --ttl 30d <dir>` uploads an immutable snapshot that remains available without the tray leader for up to 30 days; `serve --stop <token>` revokes it early.
72
73
  - **Click a file the agent mentions.** When the agent writes a file name in chat — `bb.jsh`, `check.sh`, `packages/webapp/src/main.ts:42` — SLICC checks it against the workspace in the background and turns the ones that really exist into links. Clicking opens a preview; a mention that does not resolve stays plain text, so there are no dead links. A file with uncommitted changes opens on its diff. Names are read in the context of what the agent has already DONE: after `echo "test" > /home/lars/foo.md`, a later "see foo.md" links to that exact file — even when it lives outside the workspace, and even when three other `foo.md` files exist.
74
+ - **Preview a pasted base64 payload instead of scrolling past it.** A blob pasted into chat — a screenshot as a `data:` URL, the output of `base64 < report.pdf` — collapses to a chip naming what it is and how big it is (`png · 12 KB`). Clicking opens it in the same previewer a file name does: images render, PDFs and audio play, text and JSON are syntax-highlighted. Only payloads SLICC can actually decode and recognize are collapsed; anything else stays exactly as you typed it, and long strings of every kind now wrap instead of dragging the chat column sideways.
73
75
  - **Read markdown and HTML, or read their source.** A `.md` or `.html` preview opens as the document — formatted prose, a laid-out page — with a **Source** toggle for the syntax-highlighted markup (and **Diff** when the file has uncommitted changes). HTML renders in a sandboxed frame that cannot run scripts or reach the app.
74
76
  - **Preview any file, not just the well-known types.** The previewer identifies files from their CONTENT rather than a list of extensions, so an unrecognized extension (`.jsh`, a `Justfile`) previews as source instead of refusing. Text is syntax-highlighted, PDFs render inline, and images, audio, and video play as before.
75
77
  - **Add visual and file context directly in chat.** Drop images or files onto the workspace, or use the paperclip button. Dropped `.skill` archives still install into `/workspace/skills`.
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Strip the whitespace out of `b64` and confirm the rest is decodable
3
+ * base64, returning the compacted string — or `null` if it is not base64.
4
+ *
5
+ * The single place that answers "is this string a base64 payload?". The
6
+ * `<img:data:…>` marker classifier and the transcript's base64 preview both
7
+ * gate on it, so a payload one of them decodes is a payload the other
8
+ * recognizes. The result is safe to hand to {@link base64ToUint8} in any
9
+ * runtime; padding is left exactly as the caller wrote it, since restoring
10
+ * it would rewrite payloads that are round-tripped back into `data:` URLs.
11
+ *
12
+ * URL-safe base64 (`-` / `_`) is not accepted, matching `atob`.
13
+ */
14
+ export declare function normalizeBase64(b64: string): string | null;
1
15
  /**
2
16
  * Decode a base64 string to `Uint8Array`.
3
17
  *
@@ -14,10 +28,12 @@
14
28
  * (not Node's slab pool, which `Buffer.from` draws from for small
15
29
  * inputs) — callers that read `.buffer` downstream and tests that
16
30
  * deep-equal the bytes both need this;
17
- * - the Node path validates against {@link STRICT_BASE64_RE} first and
18
- * throws on invalid input. `atob` already throws; `Buffer.from`
19
- * silently strips invalid characters, which would let bad payloads
20
- * through callers that rely on the decoder rejecting them.
31
+ * - the Node path validates against the same grammar `atob` implements
32
+ * ({@link normalizeBase64}) and throws on invalid input, then restores
33
+ * the padding `atob` would have inferred. `atob` already throws;
34
+ * `Buffer.from` silently strips invalid characters and silently refuses
35
+ * an unpadded tail, either of which would make the same payload behave
36
+ * differently depending on the runtime it landed in.
21
37
  */
22
38
  export declare function base64ToUint8(b64: string): Uint8Array<ArrayBuffer>;
23
39
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAiDA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAclE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAWvD"}
1
+ {"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AA4DA;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG1D;AAQD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAgBlE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAWvD"}
@@ -19,18 +19,51 @@ const CHUNK_SIZE = 0x8000;
19
19
  function nodeBuffer() {
20
20
  return globalThis.Buffer;
21
21
  }
22
- // Strict base64 grammar (no URL-safe variants matches `atob`). Used to
23
- // gate Node's lenient `Buffer.from('base64')` so a malformed input throws
24
- // here instead of being silently stripped: the signed-fetch transport
25
- // surfaces a malformed reply as a clean "decode failed" EIO and relies
26
- // on the decoder being strict.
22
+ // `atob`-compatible base64 grammar: padding OPTIONAL, a trailing group of
23
+ // 2 or 3 alphabet characters accepted unpadded, a remainder of 1 rejected.
24
+ // No URL-safe variants `atob` does not accept them either.
25
+ //
26
+ // Used to gate Node's lenient `Buffer.from('base64')` so a malformed input
27
+ // throws here instead of being silently stripped: the signed-fetch transport
28
+ // surfaces a malformed reply as a clean "decode failed" EIO and relies on the
29
+ // decoder rejecting it.
27
30
  //
28
31
  // Alphabet alone is not enough — `Buffer.from('abcde', 'base64')` and
29
32
  // `Buffer.from('abcd=', 'base64')` both decode successfully (and silently
30
- // drop or pad the trailing junk), while `atob` throws. Enforce the full
31
- // shape: a run of 4-char groups, optionally followed by one final group
32
- // of 2+`==`, 3+`=`, or 4 alphabet chars. Empty string is valid.
33
- const STRICT_BASE64_RE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})?$/;
33
+ // drop or pad the trailing junk), while `atob` throws. So the full shape is
34
+ // enforced: a run of 4-char groups, optionally followed by a final group of
35
+ // 2 (+`==`), 3 (+`=`), or 4 alphabet characters. Empty string is valid.
36
+ //
37
+ // The grammar describes what `atob` ACCEPTS rather than the padded form,
38
+ // because the two paths must agree: `atob` infers missing padding, so a
39
+ // stricter Node path would make the same string decode in the browser and
40
+ // throw under Node.
41
+ const ATOB_BASE64_RE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}(?:==)?|[A-Za-z0-9+/]{3}=?)?$/;
42
+ // The whitespace `atob` ignores, and that base64 producers insert to wrap
43
+ // long payloads at a column limit (`base64`(1) wraps at 76 by default).
44
+ const BASE64_WHITESPACE_RE = /[\t\n\f\r ]/g;
45
+ /**
46
+ * Strip the whitespace out of `b64` and confirm the rest is decodable
47
+ * base64, returning the compacted string — or `null` if it is not base64.
48
+ *
49
+ * The single place that answers "is this string a base64 payload?". The
50
+ * `<img:data:…>` marker classifier and the transcript's base64 preview both
51
+ * gate on it, so a payload one of them decodes is a payload the other
52
+ * recognizes. The result is safe to hand to {@link base64ToUint8} in any
53
+ * runtime; padding is left exactly as the caller wrote it, since restoring
54
+ * it would rewrite payloads that are round-tripped back into `data:` URLs.
55
+ *
56
+ * URL-safe base64 (`-` / `_`) is not accepted, matching `atob`.
57
+ */
58
+ export function normalizeBase64(b64) {
59
+ const compact = b64.replace(BASE64_WHITESPACE_RE, '');
60
+ return ATOB_BASE64_RE.test(compact) ? compact : null;
61
+ }
62
+ /** Restore the padding `atob` would have inferred, for decoders that will not. */
63
+ function padded(b64) {
64
+ const remainder = b64.length % 4;
65
+ return remainder === 0 ? b64 : b64 + '='.repeat(4 - remainder);
66
+ }
34
67
  /**
35
68
  * Decode a base64 string to `Uint8Array`.
36
69
  *
@@ -47,18 +80,23 @@ const STRICT_BASE64_RE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9
47
80
  * (not Node's slab pool, which `Buffer.from` draws from for small
48
81
  * inputs) — callers that read `.buffer` downstream and tests that
49
82
  * deep-equal the bytes both need this;
50
- * - the Node path validates against {@link STRICT_BASE64_RE} first and
51
- * throws on invalid input. `atob` already throws; `Buffer.from`
52
- * silently strips invalid characters, which would let bad payloads
53
- * through callers that rely on the decoder rejecting them.
83
+ * - the Node path validates against the same grammar `atob` implements
84
+ * ({@link normalizeBase64}) and throws on invalid input, then restores
85
+ * the padding `atob` would have inferred. `atob` already throws;
86
+ * `Buffer.from` silently strips invalid characters and silently refuses
87
+ * an unpadded tail, either of which would make the same payload behave
88
+ * differently depending on the runtime it landed in.
54
89
  */
55
90
  export function base64ToUint8(b64) {
56
91
  const B = nodeBuffer();
57
92
  if (B) {
58
- if (!STRICT_BASE64_RE.test(b64)) {
93
+ const normalized = normalizeBase64(b64);
94
+ if (normalized === null)
59
95
  throw new Error('Invalid base64 string');
60
- }
61
- return new Uint8Array(B.from(b64, 'base64'));
96
+ // `Buffer.from` will not infer the padding `atob` infers, so it is
97
+ // restored here rather than being demanded of the caller — otherwise the
98
+ // same unpadded payload decodes in the browser and throws under Node.
99
+ return new Uint8Array(B.from(padded(normalized), 'base64'));
62
100
  }
63
101
  const binary = atob(b64);
64
102
  const bytes = new Uint8Array(binary.length);
@@ -1 +1 @@
1
- {"version":3,"file":"base64.js","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,uEAAuE;AACvE,sEAAsE;AACtE,gCAAgC;AAChC,EAAE;AACF,uEAAuE;AACvE,kEAAkE;AAClE,kEAAkE;AAClE,qEAAqE;AACrE,uEAAuE;AAEvE,8CAA8C;AAC9C,sEAAsE;AACtE,wEAAwE;AACxE,qEAAqE;AACrE,MAAM,UAAU,GAAG,MAAM,CAAC;AAa1B,SAAS,UAAU;IACjB,OAAQ,UAA0C,CAAC,MAAM,CAAC;AAC5D,CAAC;AAED,yEAAyE;AACzE,0EAA0E;AAC1E,sEAAsE;AACtE,uEAAuE;AACvE,+BAA+B;AAC/B,EAAE;AACF,sEAAsE;AACtE,0EAA0E;AAC1E,wEAAwE;AACxE,wEAAwE;AACxE,gEAAgE;AAChE,MAAM,gBAAgB,GACpB,mFAAmF,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,CAAC;QACN,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QAC5E,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,KAA4B,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"base64.js","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"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"}