{"_id":"@sdelsad/commodity-desk-toolkit","_rev":"26-9d99e75b90c95f6d15a82dc0fce6709b","name":"@sdelsad/commodity-desk-toolkit","dist-tags":{"latest":"1.19.2"},"versions":{"1.0.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.0.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.0.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"2215e615b5d76db4656ca4b34633dacd6005671b","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.0.0.tgz","fileCount":5,"integrity":"sha512-5WMHhTQr82MCmej7VHrPQOiZYmPRfBTX/R8oTme1lz0poLqxQArA6tUHgiiWwZzBSZQbpLkGe1Dh7RuiEHZCsQ==","signatures":[{"sig":"MEYCIQC7+xfGDZDSQzzU/z0Ebji3d/7CjYdYSVnTjcMwd6KJ5gIhAOa/QVF0vDABYDtLfe2YeVPeehgndHEwF5VrUlDdROMf","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":15253},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Reusable audio-generation and publishing scripts for the Commodity Desk Daily podcast","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.0.0_1786385794289_0.9601832242679715","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.1.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.1.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"cbe70a106d133038a27df6a99d35fe9cea9ca92c","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.1.0.tgz","fileCount":8,"integrity":"sha512-BEHfPTKu/LkhlKEeSeOaMP0LEYGuO8DWHmWYkRVRFqDbrK8iYQZKDDm1PlMMjFbyGOT18+RC6V/kxOScVEmVNg==","signatures":[{"sig":"MEUCIQCPYp8mbswmLGhGpq9b79m8HwtGk2gch9ortOneIWjATgIgZAZwNf57niNbyVf2QllzPE5DOtGLRx7JUKUI7D8Psog=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":44745},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation, npm/CDN publishing, RSS feed, and episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.1.0_1786387695106_0.026959944741019592","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.2.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.2.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"04b4fe4c97cc58cafec0dc20cd55fda48062cd05","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.2.0.tgz","fileCount":8,"integrity":"sha512-Aws6Cl/ozkv7dsJpFsi1M7tYV+YacwZW11JmjdcTA9fx/O6bwIOYvla8luOUn7npsHDptOGY/uHdw/8lH7U8KA==","signatures":[{"sig":"MEUCIBOLOgT54vfWCmG+l2pLZ2keC4PkEpxDbHpaPTNZcHJeAiEA2MO1+0mHTnq2c5wfzoiPhYb5zimR1foEi38yO4qAFHk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45612},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.2.0_1786401742278_0.13049065814225203","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.2.1","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.2.1","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"1c1fd6f95ad6ac9c0118fddb63a7d6295c4dd123","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.2.1.tgz","fileCount":8,"integrity":"sha512-iRx2qhjIDud1fQ6Xti7h+MrMBeccNlnhayufdCctIVSBdIEG0blsjaAGfb6QqsVQSSvb1//1tVpHzuiDREA0Zw==","signatures":[{"sig":"MEUCIQDwHDugsDwg7ygN7ZnjYD0VNjk0YK9LYBJ1pzI3MPWotgIgCtmW+j9Jn+EJGS5HozzErsJA7xTZNrISNhY75fL52Cs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45889},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.2.1_1786435879788_0.027348642569714032","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.3.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.3.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"83d5b658ac18b0859bb98f2802ecae6078a3d000","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.3.0.tgz","fileCount":8,"integrity":"sha512-laOSS3wLNMJs7xXqMcKYG87W4R94WdKDQ6rMg7yLK2/mPfwpSSuaY1T4Ig1ePkvmZb8tsIPKoM1XOiq9XiD8QA==","signatures":[{"sig":"MEUCICkJSmPBGVpadSb+C94Gk9VhbNelI5j8VkRHpsUWWfzIAiEAzEkq/aB2BGWnraG/RKf4RojbHn27v6iFobKh/3ASTuQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":47164},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.3.0_1786438957282_0.09044171873091611","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.4.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.4.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"baad4b8f6eff737e673a68db459ceb003b7c306d","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.4.0.tgz","fileCount":8,"integrity":"sha512-yT2niOcFiFPLbpX5ZPeWuwuOS8sbLPneHZ3Z/mDl2JgkL6YI7U4G/WwgR/cLqWR+nlVZidkoZ0a2JBSgsrGKsw==","signatures":[{"sig":"MEUCIE2jirJbQ512BfQzlImNQ5BYD7JeAycfPcBUu4tClalYAiEAgv48+AUE9MZYmY432BiiHIipMCU+KWQPJuuFHt/cjx8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54393},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.4.0_1786440627626_0.879942367992399","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.5.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.5.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"728fff59a70d38027a7874bf201eb1cfcd86f0c8","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.5.0.tgz","fileCount":8,"integrity":"sha512-6OOJD3qHJWPIx7+Ke94INePqaVyI56WqwC8/sPiC3lcwxl2fT2o+lkshaYCGLaKFNbDCs9EIkrrzCQ2AKzQ0MA==","signatures":[{"sig":"MEQCIEgb4Bgt91TDBfghsjbJC6M/9Wl3qkQrpbCzg9G4j2/sAiAJ5oYYB0ectC5lb74dlDz32qDjGyGvVQY3Nc/2Yi9ZEw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":55560},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.5.0_1786440968670_0.11740320962015827","host":"s3://npm-registry-packages-npm-production"}},"1.6.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.6.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.6.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"cedea1256f81b2600b3b747606feef638229ec98","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.6.0.tgz","fileCount":10,"integrity":"sha512-cxGiDjPtnm8RVME0i5C46iuwNlma+fpmvl+vF88sE85lOdlhmwajIkAkZgLZLeKB5MkBaqRjobMJenAKquU4/w==","signatures":[{"sig":"MEQCIBM9tBIJw8wLmIlIPI1Ym8DTzedhzJeH5QKAEt9Em0WnAiAqDqNSF3PNd2xEpGwp/OTkMzTIoyqeaNrj5qmEaxLdgA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":76291},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.6.0_1786452148843_0.5983782159111974","host":"s3://npm-registry-packages-npm-production"}},"1.7.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.7.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.7.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"618f934bd0d046c992fb4993d9ffeb2eec49aa05","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.7.0.tgz","fileCount":10,"integrity":"sha512-IXejfOkYuhrQ/6X5D2W6db3VSgMNIfHNrl6eTpJDYofqQ259sXBPTr4mHyvte+vrDxbRbRkJsr7ysjBIMLsNRQ==","signatures":[{"sig":"MEUCIFRrNVTTLcdeAf2Qv0hWAWNGnzPN21EBAmuJy48eS34aAiEApUm4SUaF3BZnKL3ijlcYs8jAEWqycXNIvGBt18CRSbA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":79003},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.7.0_1786452425510_0.3688369037039101","host":"s3://npm-registry-packages-npm-production"}},"1.8.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.8.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.8.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"ae689ff2c2560a65684c257a95f1addc83aa189f","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.8.0.tgz","fileCount":10,"integrity":"sha512-5F4mtbQKcGliOOeYNahQ1qF6Fh/i9KET0t4dQT4up018TM4BAS56Q68hMr3sPpu13JSZsk10kH3al9A5P/0TIg==","signatures":[{"sig":"MEQCIGRdWOHiKWpY8VgsfDaGPyWaNLdtl1WCXEMVTWoB6l8ZAiBHuWJpZMGgOaGs2uWVtiopKqpEa5ncS4/ARkl2GfJKUw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81850},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.8.0_1786453188849_0.2560305505095746","host":"s3://npm-registry-packages-npm-production"}},"1.9.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.9.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.9.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"a0b38da7cd63c2c3f45772d2ae3b2b6ea7359387","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.9.0.tgz","fileCount":10,"integrity":"sha512-g76SmdoXokUtZjcY7EfMT+sI6C+JxJF0dDRH8jwMkP6NPNA/EMhhoxMQablOIBg5BP11AVNe+bCaY5ECdILGxQ==","signatures":[{"sig":"MEUCIDkguUYSE3/l9pdGy4qGH+4tXSSFiHgkIbLf63acWRuCAiEAzsIf6l8czLNv7xvgJKWhO6YtZOPWtKT9utr37yJVexI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":82613},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Commodity Desk Daily podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.9.0_1786523824001_0.6566617565829931","host":"s3://npm-registry-packages-npm-production"}},"1.10.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.10.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.10.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"8bfe056de5251fad230a6c40502a50953bfc8e87","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.10.0.tgz","fileCount":10,"integrity":"sha512-jxLHIW/viV6UvR8CHaz0U/pqCW5nn8Kw04D4E3UVh3pZM92pXKm4C8tfGjkKnaASLoG9swfTHuj5YGLld4axTw==","signatures":[{"sig":"MEYCIQDebiMLXlGXpb7wKhdCcVCn3OyS68Kh1Xo4sPodkq2BHwIhANLgviAfqEJHR0vGWcGQIHIy5jTd+49XnSzA1JOHQIfZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":83389},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Soft Commodity Trading podcast: TTS generation (Gemini via MCP or Kokoro fallback), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.10.0_1786529823271_0.08907509764944121","host":"s3://npm-registry-packages-npm-production"}},"1.11.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.11.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.11.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"f5036538826b2209afbc205c6dd9540dec05f961","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.11.0.tgz","fileCount":11,"integrity":"sha512-2VJEueRFJ71v7Y9pOcdWFgCz6mlu4r0A6edK13kYlzq2eci3XfMNReSUitwrZvz91sqUFWjXqU7Rb56yqb2G/g==","signatures":[{"sig":"MEQCIH7PNtf7BKj1pG4xFj5uU2fPmSRDwuY2+tZ1h3T3h6fWAiBGQfPzIEb02DtUHAJzY+D4xJcqJhC/CtohLov3e4ZYhw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":107812},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Soft Commodity Trading podcast: TTS generation, charts (SVG for the page, PNG for the email), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.11.0_1786532437736_0.44122866227340163","host":"s3://npm-registry-packages-npm-production"}},"1.12.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.12.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.12.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"7308a0a403014dafdb48191f50f401e73ce4cc1e","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.12.0.tgz","fileCount":11,"integrity":"sha512-WaD71u8x5ScGk/9/yq9KpUILrzrnQ9hotfIRLbFtRlV8ns6FYgYxUDccRfFbPU11W/E/9JPh+KEw6uw95HPCUw==","signatures":[{"sig":"MEQCIDBf4NEBQ/1lJ/fRgNs3p8cQ2iswNx7qAxC7PuYETO0MAiA3zA5FLIbBWkSIyCk26Klih1a278Mx2bEAiM6iRq8gVQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":110363},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Soft Commodity Trading podcast: TTS generation, charts (SVG for the page, PNG for the email), npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.12.0_1786533429615_0.14261937453570406","host":"s3://npm-registry-packages-npm-production"}},"1.13.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.13.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.13.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"e9ef47b37de5c7776d0fbdfa35c829b50c320838","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.13.0.tgz","fileCount":13,"integrity":"sha512-MOitNSzoh9IRFhESgNE/b8HDjAv64UkUAUDun0g2bj59n6cpDXoCD4Kg6d2vh5044iI7nyOhNMAqWy6o3vUi6A==","signatures":[{"sig":"MEQCIHe5Va5U1nepckYzvJx9XzBcARtcLbFW3A5R3rjTQyWLAiAbMKIXQBZ9UVYOr0bNdY8Vj6nGhd2kF3PyMD/ShJQPgQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":199533},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.5.0","description":"Scripts and show bible for the Soft Commodity Trading podcast: TTS generation, charts (SVG for the page, PNG for the email), deterministic episode page and e-mail builders, essay publishing, npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"20.12.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.13.0_1786711638774_0.6474952606656976","host":"s3://npm-registry-packages-npm-production"}},"1.13.1":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.13.1","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.13.1","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"85016929bd093774c3f8512bb3b675309f9f8e3b","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.13.1.tgz","fileCount":13,"integrity":"sha512-FBV0RoAncPTqIuhcnP6drD8LLPHEWAk6swFvCArcMdOlpiZWv1P87JEkB5KiWs7ce5N6uIhj2NHltb0CO0CJDw==","signatures":[{"sig":"MEUCIQCqgZluOE46mi5oTsI59OTHPdrcgfIihRBRCCHsuIsjewIgB/QWbW+S1UIWdatvt8UJR1lB7t/GGuwR5XkJZRYQeDQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":202223},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.5.0","description":"Scripts and show bible for the Soft Commodity Trading podcast: TTS generation, charts (SVG for the page, PNG for the email), deterministic episode page and e-mail builders, essay publishing, npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"20.12.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.13.1_1786729585999_0.4818092722386773","host":"s3://npm-registry-packages-npm-production"}},"1.13.2":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.13.2","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.13.2","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"a6c015a027b5cbeb6c8b814873a9a6e280987946","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.13.2.tgz","fileCount":13,"integrity":"sha512-bckRnBRwD/is1WBMg3nhYRf6Yr0hck1xz/b+hLZo5YIcjxfUynxAhPWE9UB+DDrYH+Emv5b0NceyYe49eA5m1w==","signatures":[{"sig":"MEUCIQD8D0xMRk4W/ofIvISkQFV/++l/Pk5KGpJU6WDUyahKZwIga3lnrRtv2uqPDphw93ErWoAIyFo3iBLs3adBN0C+SQk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":202526},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"Scripts and show bible for the Soft Commodity Trading podcast: TTS generation, charts (SVG for the page, PNG for the email), deterministic episode page and e-mail builders, essay publishing, npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.13.2_1786741290161_0.20281072025658875","host":"s3://npm-registry-packages-npm-production"}},"1.13.3":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.13.3","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.13.3","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"151e4982e410ded92fc6ec50f3ecf84537bc7898","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.13.3.tgz","fileCount":13,"integrity":"sha512-6K3V+Je/koF+Y3PezxXDIYJvMh5ODMBpRubWfM8kFNVLikXOOVK3sQjiQw6ipjFA4Ms05LOivV4+KHl0rI7ckg==","signatures":[{"sig":"MEUCIQDqTvlWg/T+Q+QKYkUOf35VwjYSZg3ZUG8JqU4o22aJjgIgeaoir3yzZzgU8m43Umgma7aL/VRHiFGzs2EpWHbDMq4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":203146},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.5.0","description":"Scripts and show bible for the Soft Commodity Trading podcast: TTS generation, charts (SVG for the page, PNG for the email), deterministic episode page and e-mail builders, essay publishing, npm/CDN/GCS publishing, RSS feed, episode continuity","directories":{},"_nodeVersion":"20.12.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.13.3_1786882462610_0.7285060756319657","host":"s3://npm-registry-packages-npm-production"}},"1.14.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.14.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.14.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"2843f68be0ecf4b01a64569d86b1563dfbed80b3","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.14.0.tgz","fileCount":14,"integrity":"sha512-kdLxUjpHQ8lD7zIjC+uWFcJIYVZBwmbh5qVe+zjYiGMDnBZ5vCLVRbn2nuZ1wdA50EV6PSxsQ4fqXYtGEe4n/g==","signatures":[{"sig":"MEQCIGI6+oXZQqWZWSO9dJlmBuKnBBgahxNcseuJui2MywJAAiAwgwthbaLesMd9QnVSXaM2MtuA+xAjLTTa8hIEZAkJAw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":214430},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"One-command publishing for the Soft Commodity Trading podcast: the daily session writes two files and runs run_episode.py, which builds the page, charts, e-mail and feed and stages them on npm for the connector to copy server-side","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.14.0_1786957477850_0.12209467082830883","host":"s3://npm-registry-packages-npm-production"}},"1.15.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.15.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.15.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"00f920da3345661c7058431ed250f51de8ee7dcd","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.15.0.tgz","fileCount":14,"integrity":"sha512-hffrPRo6JTt7C6fVnnhE8AexT7NgT9HjsUbUHwpbF+LsQNTFoAjvZDwH7JZBkxKc4eZm/2QQEb2Qo/NuFIbUPw==","signatures":[{"sig":"MEUCIDxW9N1rUhHmL4tQH/M1kBvFX4GXGRNXHZolyzyNQ1laAiEA1R7aIot5eAY2YXgnHroUJf49aMR9MxbOPIYRgDsYGcU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":215151},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"One-command publishing for the Soft Commodity Trading podcast: the daily session writes two files and runs run_episode.py, which builds the page, charts, e-mail and feed and stages them on npm for the connector to copy server-side","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.15.0_1787042160395_0.6587405845259702","host":"s3://npm-registry-packages-npm-production"}},"1.16.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.16.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.16.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"1854804c276f9350409df58b364459def91f8ed2","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.16.0.tgz","fileCount":14,"integrity":"sha512-gVRgde1bT+vxU8mk6aKGJ6/PHKZigC8ukCz/7FxCtpBKWZ09G91jqKFgAPt6XURujZcW1mDiEbUsq7JbAfprwA==","signatures":[{"sig":"MEYCIQCwi9xgThfGcUyIaCGWDSAKv0lyg6twBMiUQdKb4/sZVgIhAKGknE5uJTsph7WOau26x41nHb3iMO1sdRCny1Kxl+9O","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":218519},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"One-command publishing for the Soft Commodity Trading podcast: the daily session writes two files and runs run_episode.py, which builds the page, charts, e-mail and feed and stages them on npm for the connector to copy server-side","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.16.0_1787555956558_0.8213314806040131","host":"s3://npm-registry-packages-npm-production"}},"1.17.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.17.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.17.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"41ee867a4623e34b9890dc64ce9d54c42bfc82ff","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.17.0.tgz","fileCount":14,"integrity":"sha512-puRUGWknpu8qs7uet225IYy+i53pwO7QnkrC7mrur3FzvbSwKKFnRBZN1WRNUtH8N4ROa61P9T1dBd8M3nDQvg==","signatures":[{"sig":"MEUCIQDNy6d+KSK31Fj7c0HKO52xndxLWvsokba4by6FrEmFIgIgUcmcvFa2+FsdGdkPZlcOgC5GOaE7ZopjQkjMXUbC8f4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":219434},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"One-command publishing for the Soft Commodity Trading podcast: the daily session writes two files and runs run_episode.py, which builds the page, charts, e-mail and feed and stages them on npm for the connector to copy server-side","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.17.0_1788146928413_0.04247047011704108","host":"s3://npm-registry-packages-npm-production"}},"1.18.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.18.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.18.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"5826f61a13447c7694cd5500a6f6676e73a1196c","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.18.0.tgz","fileCount":15,"integrity":"sha512-lg1ooFh1ilFLraBN6qFqgNQ+g94+fxIA4elfXQulZRTrnsR+11S7ypi44DvUHGEGPm4E9bxwOYQib/1VA3R/9g==","signatures":[{"sig":"MEQCIHQzmonLPJ6rfA1AI8nJ6LDpp2Suo/Weu712icClaq0fAiAEbFlgxqOqr9MzyXBXe6CHvrZxsMdi34voIzANPzNBgQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":226225},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"One-command publishing for the Soft Commodity Trading podcast: the daily session writes two files and runs run_episode.py, which builds the page, charts, e-mail and feed and stages them on npm for the connector to copy server-side","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.18.0_1788147521254_0.3137729169273462","host":"s3://npm-registry-packages-npm-production"}},"1.19.0":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.19.0","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.19.0","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"773e7e6a440acf5f64b13259af2c2776ddf80e98","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.19.0.tgz","fileCount":16,"integrity":"sha512-SIZ5FT3H1cdmSXFGAYD+MI/m8/PUY0PHvhRI6OXBh/8P9M1+HRGSp7dBZNKA0hKlOAp9DYa6vepNIX854RIC4Q==","signatures":[{"sig":"MEYCIQCVrCU0WZjBKtKCA9SdWnq/t//ffDFxSjfszbOBaDgMhAIhAKXNz92AUa/EtFzpRfy9xFAAo8lM+xBRwPRQh40UK1yG","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":232178},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"One-command publishing for the Soft Commodity Trading podcast: the daily session writes two files and runs run_episode.py, which builds the page, charts, e-mail and feed and stages them on npm for the connector to copy server-side","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.19.0_1788857812097_0.8889063429080066","host":"s3://npm-registry-packages-npm-production"}},"1.19.1":{"name":"@sdelsad/commodity-desk-toolkit","version":"1.19.1","keywords":["podcast","tts","kokoro","commodities","automation"],"license":"MIT","_id":"@sdelsad/commodity-desk-toolkit@1.19.1","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"dist":{"shasum":"02521793ecd5f018bb1c29b2da0775cf3fe68f4d","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.19.1.tgz","fileCount":16,"integrity":"sha512-PxQLRIWwGv6/FCvAwjljyk+mMHrSgbIAVwRpp9t0GnjiZM3OdmpM2KFOr0JLiqph5xVy9aYbMPCm5AIYZOfh0w==","signatures":[{"sig":"MEQCIE2EDzfxJQE6iWo2QBtWitZNtD3uqycxbfhXWReZ3mL+AiBV9yWAdwYOKnO7HXYivXpQvMZCtBHmZsGKfi7sQ+ui1g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":232571},"_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"_npmVersion":"10.9.7","description":"One-command publishing for the Soft Commodity Trading podcast: the daily session writes two files and runs run_episode.py, which builds the page, charts, e-mail and feed and stages them on npm for the connector to copy server-side","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/commodity-desk-toolkit_1.19.1_1789123604029_0.8911426462542786","host":"s3://npm-registry-packages-npm-production"}},"1.19.2":{"_id":"@sdelsad/commodity-desk-toolkit@1.19.2","dist":{"shasum":"6d45f330e548f152f1f13c4dbb99a2078cae98a5","tarball":"https://registry.npmjs.org/@sdelsad/commodity-desk-toolkit/-/commodity-desk-toolkit-1.19.2.tgz","fileCount":16,"integrity":"sha512-LLcBQEvpELvbJam2bPZ9KgEDbGnYHDO2frkN6lVNDVu+rxnjVeYbUDMs3kBe6Pcb/0SBoKlJq6RYJe72IhhSzw==","signatures":[{"sig":"MEYCIQDD1fNLnjzUM0+KjTsUUwDSfhF1sGZ6chlhIByT26djZQIhANGkso68dNmMC0nzKopJskje987y6NX+AzLltq2+rFxS","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDkD0KU2K4teGh8UrCHuRhUY5tjvMgUWrBTz2oI10nzUAIgchnHqFJYWtDXnlwV6YijvKFnPcea5Gmubi1VNx5IO5I="}],"unpackedSize":233096},"name":"@sdelsad/commodity-desk-toolkit","license":"MIT","version":"1.19.2","_npmUser":{"name":"sdelsad","email":"seb.ge.ed@gmail.com"},"keywords":["podcast","tts","kokoro","commodities","automation"],"_npmVersion":"10.9.7","description":"One-command publishing for the Soft Commodity Trading podcast: the daily session writes two files and runs run_episode.py, which builds the page, charts, e-mail and feed and stages them on npm for the connector to copy server-side","directories":{},"maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/commodity-desk-toolkit_1.19.2_1789706874443_0.4828305752779489"}}},"time":{"created":"2026-08-10T18:16:34.119Z","modified":"2026-09-18T04:47:54.687Z","1.0.0":"2026-08-10T18:16:34.454Z","1.1.0":"2026-08-10T18:48:15.267Z","1.2.0":"2026-08-10T22:42:22.453Z","1.2.1":"2026-08-11T08:11:19.949Z","1.3.0":"2026-08-11T09:02:37.460Z","1.4.0":"2026-08-11T09:30:27.766Z","1.5.0":"2026-08-11T09:36:08.810Z","1.6.0":"2026-08-11T12:42:28.982Z","1.7.0":"2026-08-11T12:47:05.645Z","1.8.0":"2026-08-11T12:59:48.989Z","1.9.0":"2026-08-12T08:37:04.141Z","1.10.0":"2026-08-12T10:17:03.435Z","1.11.0":"2026-08-12T11:00:37.961Z","1.12.0":"2026-08-12T11:17:09.762Z","1.13.0":"2026-08-14T12:47:18.915Z","1.13.1":"2026-08-14T17:46:26.205Z","1.13.2":"2026-08-14T21:01:30.381Z","1.13.3":"2026-08-16T12:14:22.751Z","1.14.0":"2026-08-17T09:04:37.985Z","1.15.0":"2026-08-18T08:36:00.538Z","1.16.0":"2026-08-24T07:19:16.698Z","1.17.0":"2026-08-31T03:28:48.549Z","1.18.0":"2026-08-31T03:38:41.440Z","1.19.0":"2026-09-08T08:56:52.242Z","1.19.1":"2026-09-11T10:46:44.234Z","1.19.2":"2026-09-18T04:47:54.536Z"},"license":"MIT","keywords":["podcast","tts","kokoro","commodities","automation"],"description":"One-command publishing for the Soft Commodity Trading podcast: the daily session writes two files and runs run_episode.py, which builds the page, charts, e-mail and feed and stages them on npm for the connector to copy server-side","maintainers":[{"name":"sdelsad","email":"seb.ge.ed@gmail.com"}],"readme":"# Soft Commodity Trading — toolkit\n\nReusable, tested scripts behind the *Soft Commodity Trading* podcast: a daily\n10-minute briefing on physical commodity trading. Published so the daily\nautomation runs **the same code every morning** instead of re-deriving it.\n\n## Naming — read this before renaming anything\n\nThe show is called **Soft Commodity Trading**. It was previously *Commodity Desk\nDaily*; that name must not appear in any script, page, email or feed any more.\n\nThe **slug is frozen at `commodity-desk-daily`** on purpose. It is the npm\npackage name, the GCS folder and every published episode URL. Renaming it would\nbreak the RSS enclosure URLs of episodes already downloaded and force every\nsubscriber to re-subscribe. Display names change; the slug does not.\n\n## Install (once per fresh container)\n\n```bash\nnpm pack @sdelsad/commodity-desk-toolkit && tar xzf sdelsad-commodity-desk-toolkit-*.tgz\nbash package/setup.sh          # installs deps, downloads the Kokoro voice model (~350 MB, cached)\n```\n\nThe package also carries the show's knowledge, so the daily run reads it\ninstead of re-deriving it:\n\n- `STYLE.md` — the show bible: audience, framing rules, how it sounds, episode\n  structure, quiz standards. **Read it before writing.**\n- `curriculum.md` — 30 dense episode briefs (6 weeks): angle, worked example,\n  and the depth to reach. Two concepts per episode, or one taken deep.\n- `conversions.md` — 12 unit-conversion drills, one per episode on a rotating\n  cycle, **email only**: the rule, the fast mental method, worked examples.\n\n## What builds what\n\n| Script | In | Out |\n|---|---|---|\n| `fetch_context.py` | npm history | `covered.md`, `prev_epNN.md` |\n| `generate_audio.py` | `script.txt` | `epNN.mp3` |\n| `publish_episode.py` | `epNN.md` + mp3 | npm package, `feed.xml`, `covered.md`, `glossary.md` |\n| `build_page.py` | `epNN.md` | `epNN.html`, `epNN_chartN.png` |\n| `sw.js` | — | the offline service worker; lives once at `commodity-desk-daily/sw.js` in the bucket, pages register it relatively |\n| `build_email.py` | `epNN.md` | `epNN_email.html`, `epNN_email.txt` |\n| `build_post.py` | an essay `.md` | `writing/<slug>.html`, `index.json`, `index.html` |\n| `chart.py` | a ```chart block | inline SVG (page), PNG (e-mail) |\n\n**Write nothing by hand that one of these renders.** The page and the e-mail\nare built from the same `epNN.md`, which is why they agree; anything typed\nstraight into HTML drifts within a week.\n\n## 0. Fetch what has already aired\n\n```bash\npython3 package/fetch_context.py --episode 12 --outdir .\n```\n\nWrites `covered.md` (running log of every episode aired) and `prev_epNN.md`\n(the J-1 and J-3 episode notes, for the quiz) into the working directory, by\nwalking back through published versions. Always exits 0 — missing history is\nnormal early on and must never block an episode.\n\n## 1. Generate the audio\n\nThe spoken script is one segment per line, with the pause (in seconds) that\nshould follow it. This is what makes the narration breathe instead of\nsounding like a machine reading an article.\n\n```\nHere is the thing nobody tells you about commodity trading. ||| 0.5\nA merchant does not get paid for predicting prices. ||| 0.35\nSpace. Time. Form. ||| 0.7\n```\n\nConventions: `0.3` mid-thought, `0.5` after a completed idea, `0.7–0.8` after a\npunchline or section break. Lines starting with `#` are ignored.\n\nDialogue lines use an uppercase speaker label and are rendered with two distinct\nvoices when generated through the podcast-tts connector:\n\n```\nBUYER: November Santos, what have you got? ||| 0.25\nSELLER: I make you plus eighty-five. ||| 0.25\n```\n\n```bash\npython3 package/generate_audio.py script.txt ep02.mp3 --voice am_michael --speed 0.97\n```\n\nPrints `DURATION_SECONDS=` and `SIZE_BYTES=` for the publish step. A segment\nthat fails synthesis is skipped rather than killing the episode.\n\n## 2. Publish it\n\nPublishes to npm, which jsDelivr serves as a public CDN. Reads the currently\nlive version, bumps the patch, carries the cover art, RSS feed, `covered.md` log and `glossary.md` forward, and\nprepends the new episode to the feed (re-publishing the same episode number\nreplaces its item rather than duplicating it).\n\nTwo modes: `--mp3 <file>` embeds a local mp3 in the npm package (Kokoro\nfallback path); `--mp3-url <url> --size-bytes <n>` references an externally\nhosted mp3 (Gemini/GCS path — the feed and email point straight at it).\n\nAuthentication: export `NPM_TOKEN` and the script writes a token-free\n`.npmrc` next to the staged package, which npm expands at publish time. Never\nwrite the token itself into a file or a config command.\n\n```bash\nexport NPM_TOKEN=npm_...\npython3 package/publish_episode.py \\\n  --mp3 ep02.mp3 --notes ep02.md \\\n  --number 2 --title \"Flat Price vs Basis\" \\\n  --description \"Why physical traders don't bet on price.\" \\\n  --covered \"Basis vs flat price; long/short the basis; basis P&L. Vocab: differential, basis risk. Example: Santos cargo.\" \\\n  --glossary \"lot = one futures contract, the unit desks count positions in\" \\\n  --covered-out covered.md --glossary-out glossary.md \\\n  --duration 612 --pubdate \"Tue, 11 Aug 2026 05:00:00 GMT\"\n```\n\nAdd `--dry-run` to stage the package without publishing. Prints\n`PUBLISHED_URL=`, `PUBLISHED_VERSION=` and `FEED_URL=`.\n\n### Linking the feed to the web\n\n`--page-url` gives the episode a website. Apple Podcasts turns an item's\n`<link>` into the episode's website button and renders anchors inside a CDATA\n`<description>`, so the publisher writes both, plus an `<itunes:summary>` with\nthe bare URL for apps that strip HTML. The channel `<link>` points at the show's\nsite (`--site-url`), which is what Apple shows as the podcast's website.\n\nThe page URL is deterministic, so it can be passed in the same command that\npublishes the audio, before the page itself exists:\n\n```\n--page-url \"https://storage.googleapis.com/podcast-audio-2647223968/commodity-desk-daily/epNN.html\"\n```\n\n## 2b. Check the notes before publishing\n\n```bash\npython3 package/check_notes.py ep14.md --number 14\n```\n\nA narrow lint over `epNN.md`. Each rule encodes a mistake that actually\nshipped, so each is worth its runtime:\n\n- **Inverted unit conversions.** A tonne becomes bushels by multiplying.\n  Episode 6 shipped `30,000 t ÷ 36.744` in two solutions and was wrong by a\n  third in both. A divide that follows an `≈`, `(` or `=` is a result being\n  explained, not an input being converted, and is left alone.\n- **Spelled-out arithmetic**, recomputed and compared.\n- **Lot counts** against the bushels they claim to represent, at 5,000 a lot.\n- **Quiz numbering**: questions and answers must match, because the page pairs\n  them by number and a mismatch collapses every answer into one block.\n- **Chart JSON**, parsed; a malformed block renders as an error on the page.\n- **The old show name.**\n\nExit code 1 on any error. Never publish an episode whose solutions the lint\nrejects — wrong arithmetic in a teaching answer is the worst defect the show\ncan ship.\n\n## 3. Build the episode page\n\n```bash\npython3 package/build_page.py --notes ep03.md --number 3 \\\n  --title \"Futures Plumbing and the Shape of the Curve\" \\\n  --dek \"One sentence under the headline.\" \\\n  --audio-url \"https://.../ep03.mp3\" --duration 620 \\\n  --date \"Wednesday 12 August 2026\" --out ep03.html\n```\n\nRenders the notes into a standalone page and hosts with the connector's\n`publish_page` tool. What the page carries:\n\n- **A contents rail** built from the H2s and H3s, fixed in the left margin\n  above 1240px and inline below it, with the section being read highlighted.\n  Every heading gets a stable `id` and a hover anchor.\n- **Previous / next episode links and an archive** of every episode aired.\n- **The glossary as it stood that day**, collapsed. Episode 1 carries episode\n  1's terms, episode 5 carries one to five — a page dated Monday listing a word\n  first used on Friday reads as a leak. A search box and one chip per episode\n  narrow it further; the chips are omitted when there is only one episode to\n  show.\n- **One reveal per quiz answer** — checking Q1 no longer burns Q2 to Q7 —\n  plus reveal-all / hide-all, and a link back to each question.\n- **Tables that survive a phone**: each one scrolls inside its own box, figures\n  are tabular and right-aligned, and moves in a change column carry an explicit\n  sign and a restrained green or red.\n- `schema.org/PodcastEpisode` **JSON-LD**, Open Graph and Twitter cards using\n  the show's cover art, and a canonical URL.\n- **A print stylesheet.** Everything folded opens, the navigation and the\n  player drop out, and the glossary is left off so a printout is the article.\n- A reading-progress bar, playback-speed buttons, and the light/dark toggle.\n\n### Two files it picks up on its own\n\nBoth are optional and both are already in the working directory by the time\nthis step runs, so the command above does not mention them:\n\n| File | What it gives the page |\n|---|---|\n| `covered.md` | previous/next links with real titles, and the archive |\n| `glossary.md` | the glossary section, filtered to episodes ≤ this one |\n\nWritten by `publish_episode.py` in step 2 — which is why the page is built\nafter the publish, not before. Override with `--covered PATH` / `--glossary\nPATH`, or pass `none` to leave either out.\n\n## 3b. Build the e-mail\n\nThe daily e-mail used to be written from scratch every morning, so it drifted.\nIt is now rendered from the same `epNN.md` as the page:\n\n```bash\npython3 package/build_email.py --notes ep05.md --number 5 \\\n  --title \"Wheat: The Map and the Screens\" \\\n  --dek \"One sentence under the headline.\" \\\n  --audio-url \"https://.../ep05.mp3\" \\\n  --page-url  \"https://.../ep05.html\" \\\n  --duration 739 --date \"Friday 14 August 2026\" \\\n  --charts \"URL1,URL2,URL3\" --glossary glossary_final.md \\\n  --drill-index 5 --drill-file conversions.md \\\n  --out ep05_email.html\n```\n\nWrites `ep05_email.html` and, from the same parse, the plain-text alternative\n`ep05_email.txt`. Send both parts.\n\n- `--charts` takes the **published PNG URLs**, comma-separated, in the order\n  `build_page.py` printed its `CHART_PNG=` lines. Chart *n* in the notes gets\n  URL *n*, so a pulse chart lands in the pulse and lesson charts land inline in\n  the written edition. Missing URLs are skipped with a warning, never guessed.\n- `--drill-index` defaults to `((N − 1) mod 12) + 1`, the rotation in\n  `conversions.md`; pass it explicitly only to override the cycle.\n- `--dek` is optional and becomes the standfirst and the inbox preheader.\n\nThe section order is fixed and the builder does not let it move: header with\nthe listen button → read online → market pulse with its chart → conversion\ndrill → quiz → spoiler separator → solutions → the written edition → the\ncumulative glossary. Sections are found by their heading rather than their\nposition, so the early episodes — which ordered their notes differently — come\nout in the same order as today's.\n\nE-mail HTML is not web HTML, and the output obeys the difference: tables for\nlayout, every style inline, a 600px card with the `width` attribute set\n(Outlook ignores `max-width`), no `<style>` block, no flexbox, grid, CSS\nvariable or media query, charts as `<img>` with alt text and an explicit width,\nand a spoiler gap made of fixed-height table rows rather than stacked `<br>`,\nwhich Gmail collapses.\n\n## 3c. Publish an essay\n\nThe site's Writing section reads `writing/index.json` out of the bucket.\n\n```bash\npython3 package/build_post.py --notes essay.md --slug basis-is-a-verb \\\n  --title \"Basis Is a Verb\" --dek \"One sentence.\" \\\n  --date \"14 August 2026\" --outdir writing\n```\n\nWrites `writing/basis-is-a-verb.html`, upserts the entry in\n`writing/index.json` and rebuilds `writing/index.html`. Publish all three with\n`publish_asset` at those exact paths. Run it with no `--notes` to rebuild just\nthe index. Essays use the same renderer and palette as the episode pages.\n\n## 4. Charts\n\n`chart.py` renders a small JSON spec two ways from the same layout: an **inline\nSVG** for the page, which inherits the page's CSS variables and therefore\nfollows the light/dark toggle, and a **PNG** for the e-mail, because no mail\nclient renders inline SVG. Types: `line` (add `\"mode\":\"index\"` to rebase series\nto 100), `bar`, `waterfall`.\n\nCharts are written straight into `epNN.md` as fenced blocks:\n\n    ```chart\n    {\"type\":\"line\",\"unit\":\"c/bu\",\"title\":\"…\",\"caption\":\"…\",\"source\":\"…\",\n     \"x\":[\"4 Aug\",\"5 Aug\"],\"series\":[{\"name\":\"Dec 26\",\"values\":[471,468]}]}\n    ```\n\n`build_page.py` picks them up automatically. Add `--charts-prefix ep03_chart`\nand it also writes `ep03_chart1.png`, `ep03_chart2.png`, printing `CHART_PNG=`\nfor each, in document order. Publish those with the connector's `publish_asset`\ntool and pass the URLs to `build_email.py --charts` in the same order.\n\nTwo things worth knowing:\n\n- The SVG sizes its type in viewBox units, so it shrinks with the chart. A\n  phone renders 640 units into about 360 pixels, which would turn 12px type\n  into 7px, so the stylesheet inside the SVG scales the labels back up under a\n  width media query — and on the page the figure breaks out to the full width\n  of a small screen.\n- The PNG is only ever seen in an e-mail, scaled into a 522px column, so its\n  type is set larger than the SVG's. Titles, captions and sources wrap onto as\n  many lines as they need; the canvas grows to fit rather than clipping them.\n\nStandalone: `python3 package/chart.py spec.json --svg out.svg --png out.png`.\nPNG output needs Pillow; everything else is standard library.\n\nSee STYLE.md for when a chart earns its place.\n\n## Output\n\n- Episode audio: `https://cdn.jsdelivr.net/npm/@sdelsad/commodity-desk-daily@<version>/epNN.mp3`\n- Podcast RSS feed: `https://cdn.jsdelivr.net/npm/@sdelsad/commodity-desk-daily@latest/feed.xml`\n\n## Notes\n\n- Voices: `am_michael`, `bm_george`, `af_heart`, `am_fenrir` (Kokoro).\n- `ffmpeg` must be on PATH for the mp3 conversion.\n- jsDelivr caches aggressively; a freshly published episode can take a few\n  minutes to become fetchable, and podcast apps may see the feed later still.\n\n## Offline reading\n\nEvery page carries a **Save offline** button in the player. It registers\n`sw.js` (published once, next to the pages) and stores the page and its mp3\nin the browser's cache, so the same URL opens and plays with no connection,\nincluding on iPhone Safari. Nothing in the daily run changes: the button is\npart of `build_page.py`'s template, and `sw.js` only needs republishing if it\nis edited (`publish_from_url` to `commodity-desk-daily/sw.js`, content type\n`application/javascript`).\n\n## Server-side finalizer\n\n`publish_episode.py` accepts `--size-bytes 0 --duration 0` as placeholders\nwhen the audio has not been generated yet. The podcast-tts server's\n`finalize_episode` tool (and its hourly scheduler) then generates the audio\nfrom the script in the package, corrects the feed, and builds the page,\ncharts and e-mail with this toolkit. Never fall back to a local voice.\n","readmeFilename":"README.md"}