{"type":"session","version":3,"id":"019e7d13-ce7e-7bfc-b1e5-95dffb0b83ca","timestamp":"2026-05-31T08:08:30.590Z","cwd":"/home/sebas"}
{"type":"model_change","id":"770de3fe","parentId":null,"timestamp":"2026-05-31T08:08:33.258Z","provider":"openai-codex","modelId":"gpt-5.4"}
{"type":"thinking_level_change","id":"7f8e1fd9","parentId":"770de3fe","timestamp":"2026-05-31T08:08:33.258Z","thinkingLevel":"low"}
{"type":"message","id":"a685befc","parentId":"7f8e1fd9","timestamp":"2026-05-31T08:08:45.941Z","message":{"role":"user","content":[{"type":"text","text":"[telegram] te fijas que los links a pdfs en https://ballbox-first.emperor-ratio.ts.net/rss-offline/ se marquen leidos de forma html native?"}],"timestamp":1780214925930}}
{"type":"message","id":"435b5874","parentId":"a685befc","timestamp":"2026-05-31T08:08:50.054Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Inspecting project objectives**\n\nI need to inspect the project, likely looking into `rss-offline` and maybe some local files. I want to clarify the objective since the user might want link styling for PDF links on the page. I’ll check the repository to gather more details. It seems I should create a to-do list for active tasks to follow the instructions properly. Let’s make that to-do to keep everything organized!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bec8f54008191bc399dd302eed57b\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG-yRf7vFlBHRQEoFZsQnXx1jNtbSaSNajVFbYTQzosKC6ZRuy7IGZYzTLkaxyPt-Gj4uTzR93dSaTIhOV_cbibpR-lCBiWA4rNVZUYoKSzYxcXkCBly_-F240F-Dg98pEhYxXsnUOdFBCAOmbwGNoOPSHymc2RhIUOuokiD3PXH83G4ze3TyxFbyJckU5AlOaTmwuZLGPD-8cWptNveYQS33XF9TOHR38_W8FcL8JvtT-5VpmqexE585pD9D5QoZ1yrFv0hdKSKJkLyzL_al3G4Uyngc1LgV9xDEZUoaIh1qLNfTzraSm1BHIccLPKhdYzz_ccJnYkJ40i9oWI7HNFYMMsPYj69g9jDQEG0MmG--f3tKhCKdeCaWWtijhLeqsLvkYsGJQW6JGhlMTOPWsUtgFsgVjxWjtMXFT0Cm5iZf-SkSMexZBmLPgf0lXWipnyHso70V5Y_O37tO8C_PYcotDuj344cROZ8nARySXrwc0WRlfaBachy4grojXw5uiMyimLvyBzMURlaO_8mqwykDPQVrz6g5B1TqE0d-c1DWqdqrx-wEQMkL_dnh4FVbnHGmMTjc_qDdmeudxtBtHPdg7UehyhYaVputtcUYiTtImghRVn0Q1mZdjkpUVDEFlxqvZ3JD7U_pp9Xk0KpLc0R2OtG3ym9gf70A5R_p9K-a1aA40VYLjdjl3-MZG9hf330lbEYvCOlRHA9tvOMK_ycXPFb4wI28g6TuaerdSpnfTkaNjktndgu9Ik2uAGR6XkoN0UBb4QUEhUbzNj7tJwjIaAfTAuEWCaYUYinlKbwSKFFp6Zee_G88bwTTzGaaE8Jofxqm5dJbrCzx-hpl0w3GtQmjecHXRuuwKBGIvmJJE9W8yWt_y95WO5avIMXeJKJwb3d79iRRbW96D_N_1fEcCTYY2ydUlHlk6e0ADIf75a10no0QyXbh6y8ys4b6xMMlZO51O1Wopx9xWfHA-FmjEbM6jdYICXMrMpO8SR_YU7Fw5G9GOe5JzXGxlsORZvGoReJgwNMhuF6qrORjqjSHBAkz8fNU_O5-paxxTyJlglGSlull_QhJ-MeeEAIbKBP-RsT-lkfuUKsWHaNWSzKlZo4PWTJp_lxxM8Dn2Rt8KLiuXekuE7R5IVpGuAnN3ibyNrMXvhlO7OjVjbK57OHPxUWa9Emz4PEO-zIA2UFi6txHnNDTOpTwE_s2CSAscjMK0_D5m4FPIufmfrJTAK5l2w==\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Inspecting project objectives**\\n\\nI need to inspect the project, likely looking into `rss-offline` and maybe some local files. I want to clarify the objective since the user might want link styling for PDF links on the page. I’ll check the repository to gather more details. It seems I should create a to-do list for active tasks to follow the instructions properly. Let’s make that to-do to keep everything organized!\"}]}"},{"type":"toolCall","id":"call_6BCDbpOcXwV6OqLUkeGNhnsv|fc_0f029265420fe376016a1bec91d6888191957b4b7fa3b44b04","name":"todo","arguments":{"action":"create","subject":"Enable native visited styling for PDF links in rss-offline","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links","status":"in_progress"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":5433,"output":136,"cacheRead":0,"cacheWrite":0,"totalTokens":5569,"cost":{"input":0.0135825,"output":0.00204,"cacheRead":0,"cacheWrite":0,"total":0.015622500000000001}},"stopReason":"toolUse","timestamp":1780214925951,"responseId":"resp_0f029265420fe376016a1bec8e4d988191ae4106aa6916b430"}}
{"type":"message","id":"8d032528","parentId":"435b5874","timestamp":"2026-05-31T08:08:50.061Z","message":{"role":"toolResult","toolCallId":"call_6BCDbpOcXwV6OqLUkeGNhnsv|fc_0f029265420fe376016a1bec91d6888191957b4b7fa3b44b04","toolName":"todo","content":[{"type":"text","text":"Created #1: Enable native visited styling for PDF links in rss-offline (pending)"}],"details":{"action":"create","params":{"action":"create","subject":"Enable native visited styling for PDF links in rss-offline","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links","status":"in_progress"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"pending","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"}],"nextId":2},"isError":false,"timestamp":1780214930061}}
{"type":"message","id":"f57e8077","parentId":"8d032528","timestamp":"2026-05-31T08:08:54.005Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Finding repository**\n\nI'm considering whether I might need to search my memory for information, but it seems like that isn’t necessary right now. Instead, I think I should focus on locating the repository itself. It feels like a straightforward task, but I want to make sure I'm on the right track. I'll take the next steps to ensure I find what I'm looking for efficiently!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bec9365048191acf71e02c62d0399\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG-yVCtdHfmE4gaWkKq7oPPN-IexYxtk4rfsYSTGuGtINx0znaQj0MZXDihMIUDItCoe39u0LamDdksjr7l7tJTwWn0gFXs0zfFPzHc9ATNmdEThFij3kjr0tfmEnmoOkx2LGdjymN0WIMSHEKOL8EIthaWD-zmoIZeWOlc5Z49_IM2dkg3v-zD_SZQlXvDwBchEfrt61_v-cclwj7yDHXDsoXm6omU138GlfgZApUL-wpHaE1qpXoCbJ9HFnv4Q_zBMvLwC8xH7m0qwBnTPdWaabovspA3BHdg8ocarx9_4lNlHkYo5cATrb4uNMAO_fAOIlvW269uKtaVULwrnkuldnDCcwym09aXgzwjjVOVsx5z0bJnzcVv0yzSM8ATu1paIDMjaJOZh_oh3or-LxS6HJr7RX92SzgOvg0roVEa65jtI4xp86l94ClRuakoCehivCWwIpr4KjgINxg4Hq5KhLYSIogu0rrCQMmZtqZDNxJvUKwlIy7axXQptXkYMk44AZuKpLsttUGdyb6_lJhJ1zIaGAoYE40MquSZwkWYP5m1CNrZ99zfWlQGzk-QUJlGCgXzGnwP2PIVgu6_GVUBNTDmfFtY_SxU_n-VFrOaXRTvYSdqfyUyaODiK9TB3JtAqAYKWo6iK5eZlHQTT0hITA0S1Bv01TBxngwWm27urvNmDzFT6tDpnWWsUPy1Dz3DgM5iy9S7eESuIsUxt-3IwWyJj9Shw8sXTRr3ykEozwsIZRrV7WC59JAfpCIqzNxMTU-198PpKClH_8dbsuql2NYMtCWOvob5fA8u283tQAv4FLoo6mGUPfRiSq7PP4i4aYjICEUkcTZA0IVvPo0H3sdU33rQCIM7UZ1OIlno0eLuMcWlrTOl89GjNp1OOWGcIQPI2ggsNoW1mmuWom_GBAGzkDalTS-I7oqKOO9nJYRFVLiLDcgH8qD1Sjx_2-NOwUStr_r0F9dTSLlobbS2p55mPuLsZoxX8d3_p6ZLOlNlU=\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Finding repository**\\n\\nI'm considering whether I might need to search my memory for information, but it seems like that isn’t necessary right now. Instead, I think I should focus on locating the repository itself. It feels like a straightforward task, but I want to make sure I'm on the right track. I'll take the next steps to ensure I find what I'm looking for efficiently!\"}]}"},{"type":"toolCall","id":"call_7XCuxL2IKIsR7CimYkyYneCb|fc_0f029265420fe376016a1bec95f2508191879148da897cabee","name":"bash","arguments":{"command":"pwd && ls -1 && find /home/sebas -maxdepth 3 -type d -name '*rss*offline*' -o -type d -name 'rss-offline' 2>/dev/null | head -20","timeout":10}},{"type":"toolCall","id":"call_vffxLcGqCfoDehHSHkBesQzW|fc_0f029265420fe376016a1bec95f2648191902ed7c991fa19f0","name":"bash","arguments":{"command":"grep -R \"rss-offline\" -n /home/sebas 2>/dev/null | head -50","timeout":10}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":477,"output":143,"cacheRead":5120,"cacheWrite":0,"totalTokens":5740,"cost":{"input":0.0011925000000000002,"output":0.002145,"cacheRead":0.0012799999999999999,"cacheWrite":0,"total":0.0046175}},"stopReason":"toolUse","timestamp":1780214930061,"responseId":"resp_0f029265420fe376016a1bec9225108191b37da4ce2ba0b3be"}}
{"type":"message","id":"5f2a41ac","parentId":"f57e8077","timestamp":"2026-05-31T08:08:54.471Z","message":{"role":"toolResult","toolCallId":"call_7XCuxL2IKIsR7CimYkyYneCb|fc_0f029265420fe376016a1bec95f2508191879148da897cabee","toolName":"bash","content":[{"type":"text","text":"/home/sebas\nagents-database\nAGENTS.md\narchive\nclient_secret_1019422495774-0cvo8lefqh667vgmekec8ruqdnrior71.apps.googleusercontent.com.json\nDesktop\nDocuments\ndownloads\nDownloads\ndropbox\ngo\nmedia\nMusic\nnotes\nopencode-web.log\noutputs\npi-config\nPictures\nplaywright-browsers\nPublic\nruntime\nTemplates\nVideos\nwork\n/home/sebas/work/projects/rss-offline\n"}],"isError":false,"timestamp":1780214934471}}
{"type":"message","id":"cab371c7","parentId":"5f2a41ac","timestamp":"2026-05-31T08:08:54.471Z","message":{"role":"toolResult","toolCallId":"call_vffxLcGqCfoDehHSHkBesQzW|fc_0f029265420fe376016a1bec95f2648191902ed7c991fa19f0","toolName":"bash","content":[{"type":"text","text":"/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:515:{\"type\":\"message\",\"id\":\"555f6368\",\"parentId\":\"e2cc714f\",\"timestamp\":\"2026-05-15T09:09:21.563Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_6b9uZZyaEe7TbvQJ3cwuekDz|fc_0532075662ec7a6e016a06e2bd0e7c8190b580750f1dd86dff\",\"toolName\":\"todo\",\"content\":[{\"type\":\"text\",\"text\":\"Updated #16 (pending → in_progress)\"}],\"details\":{\"action\":\"update\",\"params\":{\"action\":\"update\",\"id\":16,\"status\":\"in_progress\"},\"tasks\":[{\"id\":1,\"subject\":\"BB-002 audit gomakethings patterns for Ballbox\",\"status\":\"completed\",\"description\":\"Research concrete frontend/offline/navigation patterns from gomakethings.com and related posts. Goal: extract practices applicable to Ballbox.\",\"activeForm\":\"researching gomakethings patterns\",\"owner\":\"agent\"},{\"id\":2,\"subject\":\"BB-003 inventory nginx-served surfaces for offline/navigation improvements\",\"status\":\"completed\",\"description\":\"Inventory current nginx-served web surfaces on ballbox-first and classify candidates for static caching, offline navigation, and resilience improvements.\",\"activeForm\":\"inventorying nginx surfaces\",\"owner\":\"agent\"},{\"id\":3,\"subject\":\"BB-004 define self-hosted RSS/offline reader product bar\",\"status\":\"completed\",\"description\":\"Define the minimal feature/product bar for a self-hosted feed reader that supports offline reading and beats the current app.\",\"activeForm\":\"defining feed reader product bar\",\"owner\":\"agent\"},{\"id\":4,\"subject\":\"BB-002 prepare reviewable Ballbox offline prototype plan\",\"status\":\"completed\",\"description\":\"Turn BB-002 research into concrete reviewable coding/testing slices for Ballbox.\",\"activeForm\":\"preparing Ballbox review slices\",\"owner\":\"agent\"},{\"id\":5,\"subject\":\"BB-003 prepare reviewable Pi web offline rollout plan\",\"status\":\"completed\",\"description\":\"Turn BB-003 research into concrete reviewable coding/testing slices for nginx-served surfaces.\",\"activeForm\":\"preparing Pi web review slices\",\"owner\":\"agent\"},{\"id\":6,\"subject\":\"BB-004 prepare OSS RSS reader shortlist and review plan\",\"status\":\"completed\",\"description\":\"Turn BB-004 research into concrete self-hosted OSS options and a test plan. No paid options.\",\"activeForm\":\"preparing RSS shortlist\",\"owner\":\"agent\"},{\"id\":7,\"subject\":\"Make copyparty file URLs more direct and preserve old /files/home links\",\"status\":\"completed\",\"description\":\"Change copyparty root mapping so /home/sebas content lives directly under /files/, and add nginx redirects for legacy /files/home/... URLs.\",\"activeForm\":\"updating copyparty paths\",\"owner\":\"agent\"},{\"id\":8,\"subject\":\"Re-focus on Ballbox QR testing\",\"status\":\"completed\",\"description\":\"Pause backlog follow-up work and switch active focus to QR testing inside Ballbox. Determine what Sebas should do next.\",\"activeForm\":\"re-focusing on QR testing\",\"owner\":\"agent\"},{\"id\":9,\"subject\":\"Fix Mercado Pago QR webhook product id mapping\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order item external_code as fallback product id so paid QR orders persist in Ballbox.\",\"activeForm\":\"fixing QR webhook mapping\",\"owner\":\"agent\"},{\"id\":10,\"subject\":\"Fix Mercado Pago QR webhook amount parsing\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order total_amount as string or number so approved QR orders persist.\",\"activeForm\":\"fixing QR amount parsing\",\"owner\":\"agent\"},{\"id\":11,\"subject\":\"Reprioritize Ballbox work away from OPS-2 and machine-dependent payments\",\"status\":\"in_progress\",\"description\":\"Skip OPS-2 and machine-dependent payment work for now. Find and run as many non-machine Ballbox tasks in parallel as possible from this machine.\",\"activeForm\":\"reprioritizing Ballbox work\",\"owner\":\"agent\"},{\"id\":12,\"subject\":\"BB-003 implement portal offline slice 1\",\"status\":\"in_progress\",\"description\":\"Harden portal service worker with explicit offline fallback and bounded caches.\",\"activeForm\":\"implementing portal offline slice\",\"owner\":\"agent\"},{\"id\":13,\"subject\":\"BB-002 prepare Ballbox public offline slice 1 for implementation\",\"status\":\"completed\",\"description\":\"Prepare exact implementation-ready slice for Ballbox public offline shell and visited-page caching.\",\"activeForm\":\"preparing Ballbox offline slice\",\"owner\":\"agent\"},{\"id\":14,\"subject\":\"BB-004 prepare FreshRSS local trial setup\",\"status\":\"in_progress\",\"description\":\"Prepare or implement a local FreshRSS trial setup path for offline-friendly feed evaluation.\",\"activeForm\":\"preparing FreshRSS trial\",\"owner\":\"agent\"},{\"id\":15,\"subject\":\"Build minimal rss-offline reader\",\"status\":\"in_progress\",\"description\":\"Replace FreshRSS as primary offline reader path with a minimal local feed reader focused on date-sorted entries and preloaded real blog links, served at /rss-offline/.\",\"activeForm\":\"building rss-offline\",\"owner\":\"agent\"},{\"id\":16,\"subject\":\"Make rss-offline local copies default and precached\",\"status\":\"in_progress\",\"description\":\"Update rss-offline so primary click opens local copies and service worker precaches generated local article pages for offline use without manual first visit.\",\"activeForm\":\"hardening rss-offline offline behavior\",\"owner\":\"agent\"}],\"nextId\":17},\"isError\":false,\"timestamp\":1778836161563}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:516:{\"type\":\"message\",\"id\":\"b2553ae4\",\"parentId\":\"555f6368\",\"timestamp\":\"2026-05-15T09:09:46.224Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"toolCall\",\"id\":\"call_SZ6rgr2JE5saH63DcfNO130R|fc_0532075662ec7a6e016a06e2c6f9d4819091bb7639082a2be9\",\"name\":\"edit\",\"arguments\":{\"path\":\"/home/sebas/work/projects/rss-offline/build.py\",\"edits\":[{\"oldText\":\"    for entry in archived:\\n        cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><a href=\\\"{escape(entry['local_path'])}\\\">copia local</a> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n\",\"newText\":\"    for entry in archived:\\n        cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['local_path'])}\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><strong>copia local</strong> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n\"},{\"oldText\":\"    sw.write_text(\\\"\\\"\\\"const V='rss-offline-v1';const CORE=V+'-core';const PAGES=V+'-pages';self.addEventListener('install',e=>{e.waitUntil(caches.open(CORE).then(c=>c.addAll(['/rss-offline/','/rss-offline/offline.html','/rss-offline/entries.json'])));self.skipWaiting();});self.addEventListener('activate',e=>{e.waitUntil(caches.keys().then(keys=>Promise.all(keys.filter(k=>!k.startsWith(V)).map(k=>caches.delete(k)))).then(()=>self.clients.claim()));});self.addEventListener('fetch',e=>{const r=e.request;const u=new URL(r.url);if(r.method!=='GET'||u.origin!==self.location.origin||!u.pathname.startsWith('/rss-offline/'))return;e.respondWith((async()=>{const cache=await caches.open(PAGES);try{const res=await fetch(r);if(res.ok) await cache.put(r,res.clone());return res;}catch{return await cache.match(r)||await caches.match('/rss-offline/offline.html')||Response.error();}})());});\\\"\\\"\\\")\\n\",\"newText\":\"    precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + [entry['local_path'] for entry in archived]\\n    sw.write_text(f\\\"\\\"\\\"const V='rss-offline-v2';const CORE=V+'-core';const PAGES=V+'-pages';const PRECACHE={json.dumps(precache_paths, ensure_ascii=False)};self.addEventListener('install',e=>{{e.waitUntil(caches.open(CORE).then(c=>c.addAll(PRECACHE)));self.skipWaiting();}});self.addEventListener('activate',e=>{{e.waitUntil(caches.keys().then(keys=>Promise.all(keys.filter(k=>!k.startsWith(V)).map(k=>caches.delete(k)))).then(()=>self.clients.claim()));}});self.addEventListener('fetch',e=>{{const r=e.request;const u=new URL(r.url);if(r.method!=='GET'||u.origin!==self.location.origin||!u.pathname.startsWith('/rss-offline/'))return;e.respondWith((async()=>{{const core=await caches.open(CORE);const pages=await caches.open(PAGES);const cached=await core.match(r)||await pages.match(r);try{{const res=await fetch(r);if(res.ok) await pages.put(r,res.clone());return res;}}catch{{return cached||await core.match('/rss-offline/offline.html')||Response.error();}}}})());}});\\\"\\\"\\\")\\n\"}]}}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":1298,\"output\":862,\"cacheRead\":217088,\"cacheWrite\":0,\"totalTokens\":219248,\"cost\":{\"input\":0.003245,\"output\":0.01293,\"cacheRead\":0.054272,\"cacheWrite\":0,\"total\":0.07044700000000001}},\"stopReason\":\"toolUse\",\"timestamp\":1778836161580,\"responseId\":\"resp_0532075662ec7a6e016a06e2c317688190a23ed7e82173cad0\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:517:{\"type\":\"message\",\"id\":\"e8f09940\",\"parentId\":\"b2553ae4\",\"timestamp\":\"2026-05-15T09:09:46.305Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_SZ6rgr2JE5saH63DcfNO130R|fc_0532075662ec7a6e016a06e2c6f9d4819091bb7639082a2be9\",\"toolName\":\"edit\",\"content\":[{\"type\":\"text\",\"text\":\"Successfully replaced 2 block(s) in /home/sebas/work/projects/rss-offline/build.py.\"}],\"details\":{\"diff\":\"     ...\\n 148     json_index.write_text(json.dumps(archived, ensure_ascii=False, indent=2))\\n 149 \\n 150     cards = []\\n 151     for entry in archived:\\n-152         cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><a href=\\\"{escape(entry['local_path'])}\\\">copia local</a> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n+152         cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['local_path'])}\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><strong>copia local</strong> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n 153 \\n 154     index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});</script></main></body></html>''')\\n 155 \\n 156     offline.write_text('<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><title>rss-offline sin conexión</title><style>body{font-family:system-ui,sans-serif;background:#07111f;color:#fff;display:grid;place-items:center;min-height:100vh;padding:24px;margin:0}main{max-width:520px;padding:24px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.05)}a{color:#8dc3ff}</style></head><body><main><h1>Sin conexión</h1><p>La lista y copias locales visitadas deberían seguir disponibles.</p><p><a href=\\\"/rss-offline/\\\">Volver a rss-offline</a></p></main></body></html>')\\n 157 \\n-158     sw.write_text(\\\"\\\"\\\"const V='rss-offline-v1';const CORE=V+'-core';const PAGES=V+'-pages';self.addEventListener('install',e=>{e.waitUntil(caches.open(CORE).then(c=>c.addAll(['/rss-offline/','/rss-offline/offline.html','/rss-offline/entries.json'])));self.skipWaiting();});self.addEventListener('activate',e=>{e.waitUntil(caches.keys().then(keys=>Promise.all(keys.filter(k=>!k.startsWith(V)).map(k=>caches.delete(k)))).then(()=>self.clients.claim()));});self.addEventListener('fetch',e=>{const r=e.request;const u=new URL(r.url);if(r.method!=='GET'||u.origin!==self.location.origin||!u.pathname.startsWith('/rss-offline/'))return;e.respondWith((async()=>{const cache=await caches.open(PAGES);try{const res=await fetch(r);if(res.ok) await cache.put(r,res.clone());return res;}catch{return await cache.match(r)||await caches.match('/rss-offline/offline.html')||Response.error();}})());});\\\"\\\"\\\")\\n+158     precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + [entry['local_path'] for entry in archived]\\n+159     sw.write_text(f\\\"\\\"\\\"const V='rss-offline-v2';const CORE=V+'-core';const PAGES=V+'-pages';const PRECACHE={json.dumps(precache_paths, ensure_ascii=False)};self.addEventListener('install',e=>{{e.waitUntil(caches.open(CORE).then(c=>c.addAll(PRECACHE)));self.skipWaiting();}});self.addEventListener('activate',e=>{{e.waitUntil(caches.keys().then(keys=>Promise.all(keys.filter(k=>!k.startsWith(V)).map(k=>caches.delete(k)))).then(()=>self.clients.claim()));}});self.addEventListener('fetch',e=>{{const r=e.request;const u=new URL(r.url);if(r.method!=='GET'||u.origin!==self.location.origin||!u.pathname.startsWith('/rss-offline/'))return;e.respondWith((async()=>{{const core=await caches.open(CORE);const pages=await caches.open(PAGES);const cached=await core.match(r)||await pages.match(r);try{{const res=await fetch(r);if(res.ok) await pages.put(r,res.clone());return res;}}catch{{return cached||await core.match('/rss-offline/offline.html')||Response.error();}}}})());}});\\\"\\\"\\\")\\n 159 \\n 160 \\n 161 if __name__ == '__main__':\\n 162     build()\",\"firstChangedLine\":152},\"isError\":false,\"timestamp\":1778836186304}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:518:{\"type\":\"message\",\"id\":\"ea1a6c85\",\"parentId\":\"e8f09940\",\"timestamp\":\"2026-05-15T09:10:01.637Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"toolCall\",\"id\":\"call_RAVVN9mY3qHdzYYGo0jkihDd|fc_0532075662ec7a6e016a06e2e23754819082c1af9238965238\",\"name\":\"bash\",\"arguments\":{\"command\":\"sudo /home/sebas/work/projects/rss-offline/build.py >/tmp/rss-offline-build.out && sed -n '1,20p' /tmp/rss-offline-build.out && python - <<'PY'\\nimport requests, re\\nidx=requests.get('https://ballbox-first.emperor-ratio.ts.net/rss-offline/',timeout=20).text\\nprint('local first', '<h2><a href=\\\"/rss-offline/article-' in idx)\\nsw=requests.get('https://ballbox-first.emperor-ratio.ts.net/rss-offline/sw.js',timeout=20).text\\nprint('v2', 'rss-offline-v2' in sw)\\nprint('precache_count', sw.count('/rss-offline/article-'))\\nPY\",\"timeout\":300}}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":2190,\"output\":180,\"cacheRead\":217088,\"cacheWrite\":0,\"totalTokens\":219458,\"cost\":{\"input\":0.005475000000000001,\"output\":0.0027,\"cacheRead\":0.054272,\"cacheWrite\":0,\"total\":0.062447}},\"stopReason\":\"toolUse\",\"timestamp\":1778836186316,\"responseId\":\"resp_0532075662ec7a6e016a06e2dade3c8190a33d48840f1837dc\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:521:{\"type\":\"message\",\"id\":\"c86c612e\",\"parentId\":\"7cac8f4b\",\"timestamp\":\"2026-05-15T09:10:45.256Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_G95GsUfyR6m814V3gMZJV9QB|fc_0532075662ec7a6e016a06e30ffb448190abce41d5a30a0e63\",\"toolName\":\"todo\",\"content\":[{\"type\":\"text\",\"text\":\"Updated #16 (in_progress → completed)\"}],\"details\":{\"action\":\"update\",\"params\":{\"action\":\"update\",\"id\":16,\"status\":\"completed\"},\"tasks\":[{\"id\":1,\"subject\":\"BB-002 audit gomakethings patterns for Ballbox\",\"status\":\"completed\",\"description\":\"Research concrete frontend/offline/navigation patterns from gomakethings.com and related posts. Goal: extract practices applicable to Ballbox.\",\"activeForm\":\"researching gomakethings patterns\",\"owner\":\"agent\"},{\"id\":2,\"subject\":\"BB-003 inventory nginx-served surfaces for offline/navigation improvements\",\"status\":\"completed\",\"description\":\"Inventory current nginx-served web surfaces on ballbox-first and classify candidates for static caching, offline navigation, and resilience improvements.\",\"activeForm\":\"inventorying nginx surfaces\",\"owner\":\"agent\"},{\"id\":3,\"subject\":\"BB-004 define self-hosted RSS/offline reader product bar\",\"status\":\"completed\",\"description\":\"Define the minimal feature/product bar for a self-hosted feed reader that supports offline reading and beats the current app.\",\"activeForm\":\"defining feed reader product bar\",\"owner\":\"agent\"},{\"id\":4,\"subject\":\"BB-002 prepare reviewable Ballbox offline prototype plan\",\"status\":\"completed\",\"description\":\"Turn BB-002 research into concrete reviewable coding/testing slices for Ballbox.\",\"activeForm\":\"preparing Ballbox review slices\",\"owner\":\"agent\"},{\"id\":5,\"subject\":\"BB-003 prepare reviewable Pi web offline rollout plan\",\"status\":\"completed\",\"description\":\"Turn BB-003 research into concrete reviewable coding/testing slices for nginx-served surfaces.\",\"activeForm\":\"preparing Pi web review slices\",\"owner\":\"agent\"},{\"id\":6,\"subject\":\"BB-004 prepare OSS RSS reader shortlist and review plan\",\"status\":\"completed\",\"description\":\"Turn BB-004 research into concrete self-hosted OSS options and a test plan. No paid options.\",\"activeForm\":\"preparing RSS shortlist\",\"owner\":\"agent\"},{\"id\":7,\"subject\":\"Make copyparty file URLs more direct and preserve old /files/home links\",\"status\":\"completed\",\"description\":\"Change copyparty root mapping so /home/sebas content lives directly under /files/, and add nginx redirects for legacy /files/home/... URLs.\",\"activeForm\":\"updating copyparty paths\",\"owner\":\"agent\"},{\"id\":8,\"subject\":\"Re-focus on Ballbox QR testing\",\"status\":\"completed\",\"description\":\"Pause backlog follow-up work and switch active focus to QR testing inside Ballbox. Determine what Sebas should do next.\",\"activeForm\":\"re-focusing on QR testing\",\"owner\":\"agent\"},{\"id\":9,\"subject\":\"Fix Mercado Pago QR webhook product id mapping\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order item external_code as fallback product id so paid QR orders persist in Ballbox.\",\"activeForm\":\"fixing QR webhook mapping\",\"owner\":\"agent\"},{\"id\":10,\"subject\":\"Fix Mercado Pago QR webhook amount parsing\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order total_amount as string or number so approved QR orders persist.\",\"activeForm\":\"fixing QR amount parsing\",\"owner\":\"agent\"},{\"id\":11,\"subject\":\"Reprioritize Ballbox work away from OPS-2 and machine-dependent payments\",\"status\":\"in_progress\",\"description\":\"Skip OPS-2 and machine-dependent payment work for now. Find and run as many non-machine Ballbox tasks in parallel as possible from this machine.\",\"activeForm\":\"reprioritizing Ballbox work\",\"owner\":\"agent\"},{\"id\":12,\"subject\":\"BB-003 implement portal offline slice 1\",\"status\":\"in_progress\",\"description\":\"Harden portal service worker with explicit offline fallback and bounded caches.\",\"activeForm\":\"implementing portal offline slice\",\"owner\":\"agent\"},{\"id\":13,\"subject\":\"BB-002 prepare Ballbox public offline slice 1 for implementation\",\"status\":\"completed\",\"description\":\"Prepare exact implementation-ready slice for Ballbox public offline shell and visited-page caching.\",\"activeForm\":\"preparing Ballbox offline slice\",\"owner\":\"agent\"},{\"id\":14,\"subject\":\"BB-004 prepare FreshRSS local trial setup\",\"status\":\"in_progress\",\"description\":\"Prepare or implement a local FreshRSS trial setup path for offline-friendly feed evaluation.\",\"activeForm\":\"preparing FreshRSS trial\",\"owner\":\"agent\"},{\"id\":15,\"subject\":\"Build minimal rss-offline reader\",\"status\":\"in_progress\",\"description\":\"Replace FreshRSS as primary offline reader path with a minimal local feed reader focused on date-sorted entries and preloaded real blog links, served at /rss-offline/.\",\"activeForm\":\"building rss-offline\",\"owner\":\"agent\"},{\"id\":16,\"subject\":\"Make rss-offline local copies default and precached\",\"status\":\"completed\",\"description\":\"Update rss-offline so primary click opens local copies and service worker precaches generated local article pages for offline use without manual first visit.\",\"activeForm\":\"hardening rss-offline offline behavior\",\"owner\":\"agent\"}],\"nextId\":17},\"isError\":false,\"timestamp\":1778836245256}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:522:{\"type\":\"message\",\"id\":\"c2f0663b\",\"parentId\":\"c86c612e\",\"timestamp\":\"2026-05-15T09:11:02.375Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"Hecho.\\n\\nCambios:\\n1. `rss-offline` ahora abre por default la **copia local**\\n- click principal = copia local\\n- link secundario = blog real\\n\\n2. service worker endurecido\\n- ahora **precacha las copias locales generadas**\\n- no hace falta abrir cada artículo una vez para que quede offline\\n\\nEstado nuevo:\\n- versión SW:\\n  - `rss-offline-v2`\\n- copias locales precargadas ahora:\\n  - 35 artículos\\n\\nQué probar:\\n1. abrir:\\n- https://ballbox-first.emperor-ratio.ts.net/rss-offline/\\n\\n2. esperar un poco a que cargue normal\\n\\n3. cortar conexión\\n\\n4. volver a entrar a:\\n- la lista\\n- uno o dos artículos locales\\n\\nAhora sí deberían abrir offline sin visita previa manual a cada uno.\",\"textSignature\":\"{\\\"v\\\":1,\\\"id\\\":\\\"msg_0532075662ec7a6e016a06e31cc81c8190bb5c6769697e1662\\\",\\\"phase\\\":\\\"final_answer\\\"}\"}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":1376,\"output\":176,\"cacheRead\":218624,\"cacheWrite\":0,\"totalTokens\":220176,\"cost\":{\"input\":0.0034400000000000003,\"output\":0.00264,\"cacheRead\":0.054655999999999996,\"cacheWrite\":0,\"total\":0.060736}},\"stopReason\":\"stop\",\"timestamp\":1778836245270,\"responseId\":\"resp_0532075662ec7a6e016a06e315fbc88190b3e6fe9087f6cea6\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:524:{\"type\":\"message\",\"id\":\"8ef0ea60\",\"parentId\":\"976dec89\",\"timestamp\":\"2026-05-15T09:39:22.245Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"**Considering service worker improvements**\\n\\nI need to keep my answer concise. I might suggest increasing the precache timing since the service worker might not wait long enough before going offline. It could be beneficial to show synchronization status on the index. The user asked if there's another way to improve, so I think a quick improvement would be adding a visible warmup status and even prefetching local articles on load. Implementing a chunked warmup from page JavaScript using fetch sounds good but requires some modification in build.py.\",\"thinkingSignature\":\"{\\\"id\\\":\\\"rs_0532075662ec7a6e016a06e9c2fac48190bc113be2ec0605d3\\\",\\\"type\\\":\\\"reasoning\\\",\\\"encrypted_content\\\":\\\"gAAAAABqBunJPbOEJafhn3cuEd0cSgyDuCs3qv9x5eZ3nZquup9ar13GaoBdccoH9I0KRiiETLv1svB7x0QUDLtRdYwIhho3ZjoRvgIe2c5FHlBrzTwGRDsbYSll7N1f-WKihpgl4KZzmJ-xUSLNmv4d0m6jqbhE60djAh_vsbcjF0iS4ReYv9_YXFAqz-pZB-2que0Oz2nCEObP7UtE1uAA6ye_Sam0ohKH73LNyKvs6hTgOPB9WU_zuEBOX7g3Oi5FYdiSsnuzFGwZX_WN0KB3Yg8knlDc9g02Qv9QsKq0rnoz5XQlyL1DprNhrYSoD2Dzz1jGJxl3pI-Mq33Ja0zy66yyaIvCqkhHk0_YTEbYObrPqnxdwAitceGjVbkxPFHccZhCUj5LDNs7Xttq9PNxB4Yv8a0BIIC_P5lVW8DFcfxAXPi_G-EoAahqRbZLHJRsar8QQEr4kml11g6Od2WWbDEF-sRP0IVMVhwq0zT42KAxD_bftLlJmagUYNUJFMnxCklBDM2q2z31OIH0gg98CdTMWq9OF2r60ShpILo7JMitfU42WrZjc12dqTFbPlNjqKjpqREKnqA_M3xhRPXHQl4EJcp9yMXTPH-P14Bz9DvYnuk_GjtZD4Cni67LcBwaH_9RWwYvsJn2FsCt4RaFaiUOEnZB5YG5B7TK4SFxJipu-PdGhPnqiNWsvG9wVSwA5qt5a7C6QNJau1DBZJh32ZMxYbBjpf1L5kvfya6Es20YIvIr7eMovGoFM1qT8BmpvgbkXoCFqPJu58ZghLQo2PtRXBa2ME6dlARoOkzGGNwaQ0rGm8LNS6RaUUSv1yJsS0sLVMtCAO5WHm3-BlbGWBs9o6V_kAjssArmrpXz_ZEX7pxciFEhEcvsqQQRpOsPBngXkYqbUx4xz9OMwyLF0KQpVsBIy1gw0ovCM5mS5uBLjHZGiPeQDhLTmeny1UteZke4gCzXZgHIbmujtKXQK9d2AW9Zv5xqafHiezHTAIKj77Lt9sYtzbr_D1AIzFBAm9Bx9QRQyHPTZcuQG9lun6W80eE7RoUrbu4fHBFyuwl4o43az23MG0bCr_3rlYKjngcX69MCa6dX7f-Bt7VW801wQTIPe2JZhREU_GNG76X3gUWwE4RJP2GdTp7jgISJDEMj6QxUgM7Tb9-QCmwjUtxTazYbYqYo2P750xvbT47WNF-xTmF-u5jKFJy2ENsTdEIzDreRXys3QkOvXCRvT5bLvp9APdxpuO_e8iP52AvtbjlU4htMR2g_stG2_ifCX9KKaQGKtQjgWXy5ymM3yI_pMMAkspjChUL-qfLfo-Mx2FEi7Ax1LnlaocpiybTaRIEcx3_pQW4JS1ZRV2XyEPqGaiu3MD84-zU-4U6peDg4KzzCL5xEDkGJYNkDdQqP1vj9oo-jqcXzES5USUgT2c4kct80F7gaf3XB1qLuezotJLWtEUttsJtI50xa_lVgbsd0G5V0hwcyMIj7Zfbb6lcvXHOW90G_IJmvz2Zdfq2I_QZPM_GlmN_Y8boINiLwG2yx-udTPX7XhfEl5R5zcTVfkHwM8P9DDlPG7DwalDKYjzcJT4xevyDkeuj3NcJ8tuNf8oPzi1SqN3wlxb28aQmAQmCiQePr1n0pD455f3rRGHaLFkCKa4r18MO8qLah8imeukzwxMg_xn3qfqQqudfCL2b2-eAjpJuWYm-AwGsSc7FOqnx0McXAllEt0aIz_cnlJeuxHaaYb8EnZE1CBVbkZRs3NoxTf8H1_xCKF43XVyzdSKR6JprEbO6_pMt8DUSZRIBcw6NvYxLiSVE_n7IAOpjwWRXwJa4Pd5HfPq-HH_B80dBaxOC-Na4W3kupNvmM0Fc81sLEnj8nWcUuoreiAcAPzaY8CW8b9bZEvK0svNpm5HcCTJgFmkJppqmVNC6ExMvVY45Fk2Ie_XEypticY9790S6LEzlR0kdM6p8voKs4YxaZ5DeLGkkCZuzCGm-gM08D2_3YJI0Q0StmZv9y_7khv402Yn7wQ8aUA0HyENgjBkrVu4LkJ1ZTO38WGGOgwzLdjYQr4hGcjpLeir8FKRWBF5uTONdeppTQpJ2Unkbdni0KjF_-8NAlUJWpjPYswXtQaEyeayGAs7njVU9VaBxawFleFpdTXaL0TKcMPI1pRRBvoDM6PZHYmGrxr3Q708Le\\\",\\\"summary\\\":[{\\\"type\\\":\\\"summary_text\\\",\\\"text\\\":\\\"**Considering service worker improvements**\\\\n\\\\nI need to keep my answer concise. I might suggest increasing the precache timing since the service worker might not wait long enough before going offline. It could be beneficial to show synchronization status on the index. The user asked if there's another way to improve, so I think a quick improvement would be adding a visible warmup status and even prefetching local articles on load. Implementing a chunked warmup from page JavaScript using fetch sounds good but requires some modification in build.py.\\\"}]}\"},{\"type\":\"toolCall\",\"id\":\"call_25RIrmwuuQ5yEJPNOsTAqxAN|fc_0532075662ec7a6e016a06e9c95554819085c134f206e462f7\",\"name\":\"todo\",\"arguments\":{\"action\":\"create\",\"subject\":\"Improve rss-offline local article warmup hit rate\",\"description\":\"Add client-side warmup fetch for local article copies so more articles are available offline without first opening them manually.\",\"activeForm\":\"improving rss-offline warmup\",\"status\":\"in_progress\",\"owner\":\"agent\"}}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":220175,\"output\":269,\"cacheRead\":0,\"cacheWrite\":0,\"totalTokens\":220444,\"cost\":{\"input\":0.5504375,\"output\":0.004035,\"cacheRead\":0,\"cacheWrite\":0,\"total\":0.5544725}},\"stopReason\":\"toolUse\",\"timestamp\":1778837951013,\"responseId\":\"resp_0532075662ec7a6e016a06e9bfa2248190ad51e1b589eec53d\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:525:{\"type\":\"message\",\"id\":\"35f31277\",\"parentId\":\"8ef0ea60\",\"timestamp\":\"2026-05-15T09:39:22.246Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_25RIrmwuuQ5yEJPNOsTAqxAN|fc_0532075662ec7a6e016a06e9c95554819085c134f206e462f7\",\"toolName\":\"todo\",\"content\":[{\"type\":\"text\",\"text\":\"Created #17: Improve rss-offline local article warmup hit rate (pending)\"}],\"details\":{\"action\":\"create\",\"params\":{\"action\":\"create\",\"subject\":\"Improve rss-offline local article warmup hit rate\",\"description\":\"Add client-side warmup fetch for local article copies so more articles are available offline without first opening them manually.\",\"activeForm\":\"improving rss-offline warmup\",\"status\":\"in_progress\",\"owner\":\"agent\"},\"tasks\":[{\"id\":1,\"subject\":\"BB-002 audit gomakethings patterns for Ballbox\",\"status\":\"completed\",\"description\":\"Research concrete frontend/offline/navigation patterns from gomakethings.com and related posts. Goal: extract practices applicable to Ballbox.\",\"activeForm\":\"researching gomakethings patterns\",\"owner\":\"agent\"},{\"id\":2,\"subject\":\"BB-003 inventory nginx-served surfaces for offline/navigation improvements\",\"status\":\"completed\",\"description\":\"Inventory current nginx-served web surfaces on ballbox-first and classify candidates for static caching, offline navigation, and resilience improvements.\",\"activeForm\":\"inventorying nginx surfaces\",\"owner\":\"agent\"},{\"id\":3,\"subject\":\"BB-004 define self-hosted RSS/offline reader product bar\",\"status\":\"completed\",\"description\":\"Define the minimal feature/product bar for a self-hosted feed reader that supports offline reading and beats the current app.\",\"activeForm\":\"defining feed reader product bar\",\"owner\":\"agent\"},{\"id\":4,\"subject\":\"BB-002 prepare reviewable Ballbox offline prototype plan\",\"status\":\"completed\",\"description\":\"Turn BB-002 research into concrete reviewable coding/testing slices for Ballbox.\",\"activeForm\":\"preparing Ballbox review slices\",\"owner\":\"agent\"},{\"id\":5,\"subject\":\"BB-003 prepare reviewable Pi web offline rollout plan\",\"status\":\"completed\",\"description\":\"Turn BB-003 research into concrete reviewable coding/testing slices for nginx-served surfaces.\",\"activeForm\":\"preparing Pi web review slices\",\"owner\":\"agent\"},{\"id\":6,\"subject\":\"BB-004 prepare OSS RSS reader shortlist and review plan\",\"status\":\"completed\",\"description\":\"Turn BB-004 research into concrete self-hosted OSS options and a test plan. No paid options.\",\"activeForm\":\"preparing RSS shortlist\",\"owner\":\"agent\"},{\"id\":7,\"subject\":\"Make copyparty file URLs more direct and preserve old /files/home links\",\"status\":\"completed\",\"description\":\"Change copyparty root mapping so /home/sebas content lives directly under /files/, and add nginx redirects for legacy /files/home/... URLs.\",\"activeForm\":\"updating copyparty paths\",\"owner\":\"agent\"},{\"id\":8,\"subject\":\"Re-focus on Ballbox QR testing\",\"status\":\"completed\",\"description\":\"Pause backlog follow-up work and switch active focus to QR testing inside Ballbox. Determine what Sebas should do next.\",\"activeForm\":\"re-focusing on QR testing\",\"owner\":\"agent\"},{\"id\":9,\"subject\":\"Fix Mercado Pago QR webhook product id mapping\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order item external_code as fallback product id so paid QR orders persist in Ballbox.\",\"activeForm\":\"fixing QR webhook mapping\",\"owner\":\"agent\"},{\"id\":10,\"subject\":\"Fix Mercado Pago QR webhook amount parsing\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order total_amount as string or number so approved QR orders persist.\",\"activeForm\":\"fixing QR amount parsing\",\"owner\":\"agent\"},{\"id\":11,\"subject\":\"Reprioritize Ballbox work away from OPS-2 and machine-dependent payments\",\"status\":\"in_progress\",\"description\":\"Skip OPS-2 and machine-dependent payment work for now. Find and run as many non-machine Ballbox tasks in parallel as possible from this machine.\",\"activeForm\":\"reprioritizing Ballbox work\",\"owner\":\"agent\"},{\"id\":12,\"subject\":\"BB-003 implement portal offline slice 1\",\"status\":\"in_progress\",\"description\":\"Harden portal service worker with explicit offline fallback and bounded caches.\",\"activeForm\":\"implementing portal offline slice\",\"owner\":\"agent\"},{\"id\":13,\"subject\":\"BB-002 prepare Ballbox public offline slice 1 for implementation\",\"status\":\"completed\",\"description\":\"Prepare exact implementation-ready slice for Ballbox public offline shell and visited-page caching.\",\"activeForm\":\"preparing Ballbox offline slice\",\"owner\":\"agent\"},{\"id\":14,\"subject\":\"BB-004 prepare FreshRSS local trial setup\",\"status\":\"in_progress\",\"description\":\"Prepare or implement a local FreshRSS trial setup path for offline-friendly feed evaluation.\",\"activeForm\":\"preparing FreshRSS trial\",\"owner\":\"agent\"},{\"id\":15,\"subject\":\"Build minimal rss-offline reader\",\"status\":\"in_progress\",\"description\":\"Replace FreshRSS as primary offline reader path with a minimal local feed reader focused on date-sorted entries and preloaded real blog links, served at /rss-offline/.\",\"activeForm\":\"building rss-offline\",\"owner\":\"agent\"},{\"id\":16,\"subject\":\"Make rss-offline local copies default and precached\",\"status\":\"completed\",\"description\":\"Update rss-offline so primary click opens local copies and service worker precaches generated local article pages for offline use without manual first visit.\",\"activeForm\":\"hardening rss-offline offline behavior\",\"owner\":\"agent\"},{\"id\":17,\"subject\":\"Improve rss-offline local article warmup hit rate\",\"status\":\"pending\",\"description\":\"Add client-side warmup fetch for local article copies so more articles are available offline without first opening them manually.\",\"activeForm\":\"improving rss-offline warmup\",\"owner\":\"agent\"}],\"nextId\":18},\"isError\":false,\"timestamp\":1778837962246}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:527:{\"type\":\"message\",\"id\":\"d6d6d369\",\"parentId\":\"7a358f30\",\"timestamp\":\"2026-05-15T09:39:28.747Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_bebenDTT5lviabTT75AcUba4|fc_0532075662ec7a6e016a06e9cf3d24819090df420e75907f89\",\"toolName\":\"todo\",\"content\":[{\"type\":\"text\",\"text\":\"Updated #17 (pending → in_progress)\"}],\"details\":{\"action\":\"update\",\"params\":{\"action\":\"update\",\"id\":17,\"status\":\"in_progress\"},\"tasks\":[{\"id\":1,\"subject\":\"BB-002 audit gomakethings patterns for Ballbox\",\"status\":\"completed\",\"description\":\"Research concrete frontend/offline/navigation patterns from gomakethings.com and related posts. Goal: extract practices applicable to Ballbox.\",\"activeForm\":\"researching gomakethings patterns\",\"owner\":\"agent\"},{\"id\":2,\"subject\":\"BB-003 inventory nginx-served surfaces for offline/navigation improvements\",\"status\":\"completed\",\"description\":\"Inventory current nginx-served web surfaces on ballbox-first and classify candidates for static caching, offline navigation, and resilience improvements.\",\"activeForm\":\"inventorying nginx surfaces\",\"owner\":\"agent\"},{\"id\":3,\"subject\":\"BB-004 define self-hosted RSS/offline reader product bar\",\"status\":\"completed\",\"description\":\"Define the minimal feature/product bar for a self-hosted feed reader that supports offline reading and beats the current app.\",\"activeForm\":\"defining feed reader product bar\",\"owner\":\"agent\"},{\"id\":4,\"subject\":\"BB-002 prepare reviewable Ballbox offline prototype plan\",\"status\":\"completed\",\"description\":\"Turn BB-002 research into concrete reviewable coding/testing slices for Ballbox.\",\"activeForm\":\"preparing Ballbox review slices\",\"owner\":\"agent\"},{\"id\":5,\"subject\":\"BB-003 prepare reviewable Pi web offline rollout plan\",\"status\":\"completed\",\"description\":\"Turn BB-003 research into concrete reviewable coding/testing slices for nginx-served surfaces.\",\"activeForm\":\"preparing Pi web review slices\",\"owner\":\"agent\"},{\"id\":6,\"subject\":\"BB-004 prepare OSS RSS reader shortlist and review plan\",\"status\":\"completed\",\"description\":\"Turn BB-004 research into concrete self-hosted OSS options and a test plan. No paid options.\",\"activeForm\":\"preparing RSS shortlist\",\"owner\":\"agent\"},{\"id\":7,\"subject\":\"Make copyparty file URLs more direct and preserve old /files/home links\",\"status\":\"completed\",\"description\":\"Change copyparty root mapping so /home/sebas content lives directly under /files/, and add nginx redirects for legacy /files/home/... URLs.\",\"activeForm\":\"updating copyparty paths\",\"owner\":\"agent\"},{\"id\":8,\"subject\":\"Re-focus on Ballbox QR testing\",\"status\":\"completed\",\"description\":\"Pause backlog follow-up work and switch active focus to QR testing inside Ballbox. Determine what Sebas should do next.\",\"activeForm\":\"re-focusing on QR testing\",\"owner\":\"agent\"},{\"id\":9,\"subject\":\"Fix Mercado Pago QR webhook product id mapping\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order item external_code as fallback product id so paid QR orders persist in Ballbox.\",\"activeForm\":\"fixing QR webhook mapping\",\"owner\":\"agent\"},{\"id\":10,\"subject\":\"Fix Mercado Pago QR webhook amount parsing\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order total_amount as string or number so approved QR orders persist.\",\"activeForm\":\"fixing QR amount parsing\",\"owner\":\"agent\"},{\"id\":11,\"subject\":\"Reprioritize Ballbox work away from OPS-2 and machine-dependent payments\",\"status\":\"in_progress\",\"description\":\"Skip OPS-2 and machine-dependent payment work for now. Find and run as many non-machine Ballbox tasks in parallel as possible from this machine.\",\"activeForm\":\"reprioritizing Ballbox work\",\"owner\":\"agent\"},{\"id\":12,\"subject\":\"BB-003 implement portal offline slice 1\",\"status\":\"in_progress\",\"description\":\"Harden portal service worker with explicit offline fallback and bounded caches.\",\"activeForm\":\"implementing portal offline slice\",\"owner\":\"agent\"},{\"id\":13,\"subject\":\"BB-002 prepare Ballbox public offline slice 1 for implementation\",\"status\":\"completed\",\"description\":\"Prepare exact implementation-ready slice for Ballbox public offline shell and visited-page caching.\",\"activeForm\":\"preparing Ballbox offline slice\",\"owner\":\"agent\"},{\"id\":14,\"subject\":\"BB-004 prepare FreshRSS local trial setup\",\"status\":\"in_progress\",\"description\":\"Prepare or implement a local FreshRSS trial setup path for offline-friendly feed evaluation.\",\"activeForm\":\"preparing FreshRSS trial\",\"owner\":\"agent\"},{\"id\":15,\"subject\":\"Build minimal rss-offline reader\",\"status\":\"in_progress\",\"description\":\"Replace FreshRSS as primary offline reader path with a minimal local feed reader focused on date-sorted entries and preloaded real blog links, served at /rss-offline/.\",\"activeForm\":\"building rss-offline\",\"owner\":\"agent\"},{\"id\":16,\"subject\":\"Make rss-offline local copies default and precached\",\"status\":\"completed\",\"description\":\"Update rss-offline so primary click opens local copies and service worker precaches generated local article pages for offline use without manual first visit.\",\"activeForm\":\"hardening rss-offline offline behavior\",\"owner\":\"agent\"},{\"id\":17,\"subject\":\"Improve rss-offline local article warmup hit rate\",\"status\":\"in_progress\",\"description\":\"Add client-side warmup fetch for local article copies so more articles are available offline without first opening them manually.\",\"activeForm\":\"improving rss-offline warmup\",\"owner\":\"agent\"}],\"nextId\":18},\"isError\":false,\"timestamp\":1778837968747}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:528:{\"type\":\"message\",\"id\":\"5406553d\",\"parentId\":\"d6d6d369\",\"timestamp\":\"2026-05-15T09:39:52.762Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"toolCall\",\"id\":\"call_nWaUIV9VL4H69l2pZPtbI2V7|fc_0532075662ec7a6e016a06e9d427488190897ebf6c01e95cc4\",\"name\":\"edit\",\"arguments\":{\"path\":\"/home/sebas/work/projects/rss-offline/build.py\",\"edits\":[{\"oldText\":\"    index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});</script></main></body></html>''')\\n\",\"newText\":\"    local_paths = [entry['local_path'] for entry in archived]\\n    index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\\n\"}]}}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":1380,\"output\":1074,\"cacheRead\":219136,\"cacheWrite\":0,\"totalTokens\":221590,\"cost\":{\"input\":0.0034500000000000004,\"output\":0.01611,\"cacheRead\":0.054784,\"cacheWrite\":0,\"total\":0.074344}},\"stopReason\":\"toolUse\",\"timestamp\":1778837968757,\"responseId\":\"resp_0532075662ec7a6e016a06e9d15fb8819085533c8e6c1c4920\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:529:{\"type\":\"message\",\"id\":\"0669fc39\",\"parentId\":\"5406553d\",\"timestamp\":\"2026-05-15T09:39:52.865Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_nWaUIV9VL4H69l2pZPtbI2V7|fc_0532075662ec7a6e016a06e9d427488190897ebf6c01e95cc4\",\"toolName\":\"edit\",\"content\":[{\"type\":\"text\",\"text\":\"Successfully replaced 1 block(s) in /home/sebas/work/projects/rss-offline/build.py.\"}],\"details\":{\"diff\":\"     ...\\n 150     cards = []\\n 151     for entry in archived:\\n 152         cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['local_path'])}\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><strong>copia local</strong> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n 153 \\n-154     index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});</script></main></body></html>''')\\n+154     local_paths = [entry['local_path'] for entry in archived]\\n+155     index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\\n 155 \\n 156     offline.write_text('<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><title>rss-offline sin conexión</title><style>body{font-family:system-ui,sans-serif;background:#07111f;color:#fff;display:grid;place-items:center;min-height:100vh;padding:24px;margin:0}main{max-width:520px;padding:24px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.05)}a{color:#8dc3ff}</style></head><body><main><h1>Sin conexión</h1><p>La lista y copias locales visitadas deberían seguir disponibles.</p><p><a href=\\\"/rss-offline/\\\">Volver a rss-offline</a></p></main></body></html>')\\n 157 \\n 158     precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + [entry['local_path'] for entry in archived]\\n     ...\",\"firstChangedLine\":154},\"isError\":false,\"timestamp\":1778837992863}}\n\n[Showing lines 40-50 of 50 (50.0KB limit). Full output: /tmp/pi-bash-ec64b4c83565caf5.log]"}],"details":{"truncation":{"content":"/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:515:{\"type\":\"message\",\"id\":\"555f6368\",\"parentId\":\"e2cc714f\",\"timestamp\":\"2026-05-15T09:09:21.563Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_6b9uZZyaEe7TbvQJ3cwuekDz|fc_0532075662ec7a6e016a06e2bd0e7c8190b580750f1dd86dff\",\"toolName\":\"todo\",\"content\":[{\"type\":\"text\",\"text\":\"Updated #16 (pending → in_progress)\"}],\"details\":{\"action\":\"update\",\"params\":{\"action\":\"update\",\"id\":16,\"status\":\"in_progress\"},\"tasks\":[{\"id\":1,\"subject\":\"BB-002 audit gomakethings patterns for Ballbox\",\"status\":\"completed\",\"description\":\"Research concrete frontend/offline/navigation patterns from gomakethings.com and related posts. Goal: extract practices applicable to Ballbox.\",\"activeForm\":\"researching gomakethings patterns\",\"owner\":\"agent\"},{\"id\":2,\"subject\":\"BB-003 inventory nginx-served surfaces for offline/navigation improvements\",\"status\":\"completed\",\"description\":\"Inventory current nginx-served web surfaces on ballbox-first and classify candidates for static caching, offline navigation, and resilience improvements.\",\"activeForm\":\"inventorying nginx surfaces\",\"owner\":\"agent\"},{\"id\":3,\"subject\":\"BB-004 define self-hosted RSS/offline reader product bar\",\"status\":\"completed\",\"description\":\"Define the minimal feature/product bar for a self-hosted feed reader that supports offline reading and beats the current app.\",\"activeForm\":\"defining feed reader product bar\",\"owner\":\"agent\"},{\"id\":4,\"subject\":\"BB-002 prepare reviewable Ballbox offline prototype plan\",\"status\":\"completed\",\"description\":\"Turn BB-002 research into concrete reviewable coding/testing slices for Ballbox.\",\"activeForm\":\"preparing Ballbox review slices\",\"owner\":\"agent\"},{\"id\":5,\"subject\":\"BB-003 prepare reviewable Pi web offline rollout plan\",\"status\":\"completed\",\"description\":\"Turn BB-003 research into concrete reviewable coding/testing slices for nginx-served surfaces.\",\"activeForm\":\"preparing Pi web review slices\",\"owner\":\"agent\"},{\"id\":6,\"subject\":\"BB-004 prepare OSS RSS reader shortlist and review plan\",\"status\":\"completed\",\"description\":\"Turn BB-004 research into concrete self-hosted OSS options and a test plan. No paid options.\",\"activeForm\":\"preparing RSS shortlist\",\"owner\":\"agent\"},{\"id\":7,\"subject\":\"Make copyparty file URLs more direct and preserve old /files/home links\",\"status\":\"completed\",\"description\":\"Change copyparty root mapping so /home/sebas content lives directly under /files/, and add nginx redirects for legacy /files/home/... URLs.\",\"activeForm\":\"updating copyparty paths\",\"owner\":\"agent\"},{\"id\":8,\"subject\":\"Re-focus on Ballbox QR testing\",\"status\":\"completed\",\"description\":\"Pause backlog follow-up work and switch active focus to QR testing inside Ballbox. Determine what Sebas should do next.\",\"activeForm\":\"re-focusing on QR testing\",\"owner\":\"agent\"},{\"id\":9,\"subject\":\"Fix Mercado Pago QR webhook product id mapping\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order item external_code as fallback product id so paid QR orders persist in Ballbox.\",\"activeForm\":\"fixing QR webhook mapping\",\"owner\":\"agent\"},{\"id\":10,\"subject\":\"Fix Mercado Pago QR webhook amount parsing\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order total_amount as string or number so approved QR orders persist.\",\"activeForm\":\"fixing QR amount parsing\",\"owner\":\"agent\"},{\"id\":11,\"subject\":\"Reprioritize Ballbox work away from OPS-2 and machine-dependent payments\",\"status\":\"in_progress\",\"description\":\"Skip OPS-2 and machine-dependent payment work for now. Find and run as many non-machine Ballbox tasks in parallel as possible from this machine.\",\"activeForm\":\"reprioritizing Ballbox work\",\"owner\":\"agent\"},{\"id\":12,\"subject\":\"BB-003 implement portal offline slice 1\",\"status\":\"in_progress\",\"description\":\"Harden portal service worker with explicit offline fallback and bounded caches.\",\"activeForm\":\"implementing portal offline slice\",\"owner\":\"agent\"},{\"id\":13,\"subject\":\"BB-002 prepare Ballbox public offline slice 1 for implementation\",\"status\":\"completed\",\"description\":\"Prepare exact implementation-ready slice for Ballbox public offline shell and visited-page caching.\",\"activeForm\":\"preparing Ballbox offline slice\",\"owner\":\"agent\"},{\"id\":14,\"subject\":\"BB-004 prepare FreshRSS local trial setup\",\"status\":\"in_progress\",\"description\":\"Prepare or implement a local FreshRSS trial setup path for offline-friendly feed evaluation.\",\"activeForm\":\"preparing FreshRSS trial\",\"owner\":\"agent\"},{\"id\":15,\"subject\":\"Build minimal rss-offline reader\",\"status\":\"in_progress\",\"description\":\"Replace FreshRSS as primary offline reader path with a minimal local feed reader focused on date-sorted entries and preloaded real blog links, served at /rss-offline/.\",\"activeForm\":\"building rss-offline\",\"owner\":\"agent\"},{\"id\":16,\"subject\":\"Make rss-offline local copies default and precached\",\"status\":\"in_progress\",\"description\":\"Update rss-offline so primary click opens local copies and service worker precaches generated local article pages for offline use without manual first visit.\",\"activeForm\":\"hardening rss-offline offline behavior\",\"owner\":\"agent\"}],\"nextId\":17},\"isError\":false,\"timestamp\":1778836161563}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:516:{\"type\":\"message\",\"id\":\"b2553ae4\",\"parentId\":\"555f6368\",\"timestamp\":\"2026-05-15T09:09:46.224Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"toolCall\",\"id\":\"call_SZ6rgr2JE5saH63DcfNO130R|fc_0532075662ec7a6e016a06e2c6f9d4819091bb7639082a2be9\",\"name\":\"edit\",\"arguments\":{\"path\":\"/home/sebas/work/projects/rss-offline/build.py\",\"edits\":[{\"oldText\":\"    for entry in archived:\\n        cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><a href=\\\"{escape(entry['local_path'])}\\\">copia local</a> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n\",\"newText\":\"    for entry in archived:\\n        cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['local_path'])}\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><strong>copia local</strong> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n\"},{\"oldText\":\"    sw.write_text(\\\"\\\"\\\"const V='rss-offline-v1';const CORE=V+'-core';const PAGES=V+'-pages';self.addEventListener('install',e=>{e.waitUntil(caches.open(CORE).then(c=>c.addAll(['/rss-offline/','/rss-offline/offline.html','/rss-offline/entries.json'])));self.skipWaiting();});self.addEventListener('activate',e=>{e.waitUntil(caches.keys().then(keys=>Promise.all(keys.filter(k=>!k.startsWith(V)).map(k=>caches.delete(k)))).then(()=>self.clients.claim()));});self.addEventListener('fetch',e=>{const r=e.request;const u=new URL(r.url);if(r.method!=='GET'||u.origin!==self.location.origin||!u.pathname.startsWith('/rss-offline/'))return;e.respondWith((async()=>{const cache=await caches.open(PAGES);try{const res=await fetch(r);if(res.ok) await cache.put(r,res.clone());return res;}catch{return await cache.match(r)||await caches.match('/rss-offline/offline.html')||Response.error();}})());});\\\"\\\"\\\")\\n\",\"newText\":\"    precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + [entry['local_path'] for entry in archived]\\n    sw.write_text(f\\\"\\\"\\\"const V='rss-offline-v2';const CORE=V+'-core';const PAGES=V+'-pages';const PRECACHE={json.dumps(precache_paths, ensure_ascii=False)};self.addEventListener('install',e=>{{e.waitUntil(caches.open(CORE).then(c=>c.addAll(PRECACHE)));self.skipWaiting();}});self.addEventListener('activate',e=>{{e.waitUntil(caches.keys().then(keys=>Promise.all(keys.filter(k=>!k.startsWith(V)).map(k=>caches.delete(k)))).then(()=>self.clients.claim()));}});self.addEventListener('fetch',e=>{{const r=e.request;const u=new URL(r.url);if(r.method!=='GET'||u.origin!==self.location.origin||!u.pathname.startsWith('/rss-offline/'))return;e.respondWith((async()=>{{const core=await caches.open(CORE);const pages=await caches.open(PAGES);const cached=await core.match(r)||await pages.match(r);try{{const res=await fetch(r);if(res.ok) await pages.put(r,res.clone());return res;}}catch{{return cached||await core.match('/rss-offline/offline.html')||Response.error();}}}})());}});\\\"\\\"\\\")\\n\"}]}}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":1298,\"output\":862,\"cacheRead\":217088,\"cacheWrite\":0,\"totalTokens\":219248,\"cost\":{\"input\":0.003245,\"output\":0.01293,\"cacheRead\":0.054272,\"cacheWrite\":0,\"total\":0.07044700000000001}},\"stopReason\":\"toolUse\",\"timestamp\":1778836161580,\"responseId\":\"resp_0532075662ec7a6e016a06e2c317688190a23ed7e82173cad0\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:517:{\"type\":\"message\",\"id\":\"e8f09940\",\"parentId\":\"b2553ae4\",\"timestamp\":\"2026-05-15T09:09:46.305Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_SZ6rgr2JE5saH63DcfNO130R|fc_0532075662ec7a6e016a06e2c6f9d4819091bb7639082a2be9\",\"toolName\":\"edit\",\"content\":[{\"type\":\"text\",\"text\":\"Successfully replaced 2 block(s) in /home/sebas/work/projects/rss-offline/build.py.\"}],\"details\":{\"diff\":\"     ...\\n 148     json_index.write_text(json.dumps(archived, ensure_ascii=False, indent=2))\\n 149 \\n 150     cards = []\\n 151     for entry in archived:\\n-152         cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><a href=\\\"{escape(entry['local_path'])}\\\">copia local</a> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n+152         cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['local_path'])}\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><strong>copia local</strong> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n 153 \\n 154     index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});</script></main></body></html>''')\\n 155 \\n 156     offline.write_text('<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><title>rss-offline sin conexión</title><style>body{font-family:system-ui,sans-serif;background:#07111f;color:#fff;display:grid;place-items:center;min-height:100vh;padding:24px;margin:0}main{max-width:520px;padding:24px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.05)}a{color:#8dc3ff}</style></head><body><main><h1>Sin conexión</h1><p>La lista y copias locales visitadas deberían seguir disponibles.</p><p><a href=\\\"/rss-offline/\\\">Volver a rss-offline</a></p></main></body></html>')\\n 157 \\n-158     sw.write_text(\\\"\\\"\\\"const V='rss-offline-v1';const CORE=V+'-core';const PAGES=V+'-pages';self.addEventListener('install',e=>{e.waitUntil(caches.open(CORE).then(c=>c.addAll(['/rss-offline/','/rss-offline/offline.html','/rss-offline/entries.json'])));self.skipWaiting();});self.addEventListener('activate',e=>{e.waitUntil(caches.keys().then(keys=>Promise.all(keys.filter(k=>!k.startsWith(V)).map(k=>caches.delete(k)))).then(()=>self.clients.claim()));});self.addEventListener('fetch',e=>{const r=e.request;const u=new URL(r.url);if(r.method!=='GET'||u.origin!==self.location.origin||!u.pathname.startsWith('/rss-offline/'))return;e.respondWith((async()=>{const cache=await caches.open(PAGES);try{const res=await fetch(r);if(res.ok) await cache.put(r,res.clone());return res;}catch{return await cache.match(r)||await caches.match('/rss-offline/offline.html')||Response.error();}})());});\\\"\\\"\\\")\\n+158     precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + [entry['local_path'] for entry in archived]\\n+159     sw.write_text(f\\\"\\\"\\\"const V='rss-offline-v2';const CORE=V+'-core';const PAGES=V+'-pages';const PRECACHE={json.dumps(precache_paths, ensure_ascii=False)};self.addEventListener('install',e=>{{e.waitUntil(caches.open(CORE).then(c=>c.addAll(PRECACHE)));self.skipWaiting();}});self.addEventListener('activate',e=>{{e.waitUntil(caches.keys().then(keys=>Promise.all(keys.filter(k=>!k.startsWith(V)).map(k=>caches.delete(k)))).then(()=>self.clients.claim()));}});self.addEventListener('fetch',e=>{{const r=e.request;const u=new URL(r.url);if(r.method!=='GET'||u.origin!==self.location.origin||!u.pathname.startsWith('/rss-offline/'))return;e.respondWith((async()=>{{const core=await caches.open(CORE);const pages=await caches.open(PAGES);const cached=await core.match(r)||await pages.match(r);try{{const res=await fetch(r);if(res.ok) await pages.put(r,res.clone());return res;}}catch{{return cached||await core.match('/rss-offline/offline.html')||Response.error();}}}})());}});\\\"\\\"\\\")\\n 159 \\n 160 \\n 161 if __name__ == '__main__':\\n 162     build()\",\"firstChangedLine\":152},\"isError\":false,\"timestamp\":1778836186304}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:518:{\"type\":\"message\",\"id\":\"ea1a6c85\",\"parentId\":\"e8f09940\",\"timestamp\":\"2026-05-15T09:10:01.637Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"toolCall\",\"id\":\"call_RAVVN9mY3qHdzYYGo0jkihDd|fc_0532075662ec7a6e016a06e2e23754819082c1af9238965238\",\"name\":\"bash\",\"arguments\":{\"command\":\"sudo /home/sebas/work/projects/rss-offline/build.py >/tmp/rss-offline-build.out && sed -n '1,20p' /tmp/rss-offline-build.out && python - <<'PY'\\nimport requests, re\\nidx=requests.get('https://ballbox-first.emperor-ratio.ts.net/rss-offline/',timeout=20).text\\nprint('local first', '<h2><a href=\\\"/rss-offline/article-' in idx)\\nsw=requests.get('https://ballbox-first.emperor-ratio.ts.net/rss-offline/sw.js',timeout=20).text\\nprint('v2', 'rss-offline-v2' in sw)\\nprint('precache_count', sw.count('/rss-offline/article-'))\\nPY\",\"timeout\":300}}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":2190,\"output\":180,\"cacheRead\":217088,\"cacheWrite\":0,\"totalTokens\":219458,\"cost\":{\"input\":0.005475000000000001,\"output\":0.0027,\"cacheRead\":0.054272,\"cacheWrite\":0,\"total\":0.062447}},\"stopReason\":\"toolUse\",\"timestamp\":1778836186316,\"responseId\":\"resp_0532075662ec7a6e016a06e2dade3c8190a33d48840f1837dc\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:521:{\"type\":\"message\",\"id\":\"c86c612e\",\"parentId\":\"7cac8f4b\",\"timestamp\":\"2026-05-15T09:10:45.256Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_G95GsUfyR6m814V3gMZJV9QB|fc_0532075662ec7a6e016a06e30ffb448190abce41d5a30a0e63\",\"toolName\":\"todo\",\"content\":[{\"type\":\"text\",\"text\":\"Updated #16 (in_progress → completed)\"}],\"details\":{\"action\":\"update\",\"params\":{\"action\":\"update\",\"id\":16,\"status\":\"completed\"},\"tasks\":[{\"id\":1,\"subject\":\"BB-002 audit gomakethings patterns for Ballbox\",\"status\":\"completed\",\"description\":\"Research concrete frontend/offline/navigation patterns from gomakethings.com and related posts. Goal: extract practices applicable to Ballbox.\",\"activeForm\":\"researching gomakethings patterns\",\"owner\":\"agent\"},{\"id\":2,\"subject\":\"BB-003 inventory nginx-served surfaces for offline/navigation improvements\",\"status\":\"completed\",\"description\":\"Inventory current nginx-served web surfaces on ballbox-first and classify candidates for static caching, offline navigation, and resilience improvements.\",\"activeForm\":\"inventorying nginx surfaces\",\"owner\":\"agent\"},{\"id\":3,\"subject\":\"BB-004 define self-hosted RSS/offline reader product bar\",\"status\":\"completed\",\"description\":\"Define the minimal feature/product bar for a self-hosted feed reader that supports offline reading and beats the current app.\",\"activeForm\":\"defining feed reader product bar\",\"owner\":\"agent\"},{\"id\":4,\"subject\":\"BB-002 prepare reviewable Ballbox offline prototype plan\",\"status\":\"completed\",\"description\":\"Turn BB-002 research into concrete reviewable coding/testing slices for Ballbox.\",\"activeForm\":\"preparing Ballbox review slices\",\"owner\":\"agent\"},{\"id\":5,\"subject\":\"BB-003 prepare reviewable Pi web offline rollout plan\",\"status\":\"completed\",\"description\":\"Turn BB-003 research into concrete reviewable coding/testing slices for nginx-served surfaces.\",\"activeForm\":\"preparing Pi web review slices\",\"owner\":\"agent\"},{\"id\":6,\"subject\":\"BB-004 prepare OSS RSS reader shortlist and review plan\",\"status\":\"completed\",\"description\":\"Turn BB-004 research into concrete self-hosted OSS options and a test plan. No paid options.\",\"activeForm\":\"preparing RSS shortlist\",\"owner\":\"agent\"},{\"id\":7,\"subject\":\"Make copyparty file URLs more direct and preserve old /files/home links\",\"status\":\"completed\",\"description\":\"Change copyparty root mapping so /home/sebas content lives directly under /files/, and add nginx redirects for legacy /files/home/... URLs.\",\"activeForm\":\"updating copyparty paths\",\"owner\":\"agent\"},{\"id\":8,\"subject\":\"Re-focus on Ballbox QR testing\",\"status\":\"completed\",\"description\":\"Pause backlog follow-up work and switch active focus to QR testing inside Ballbox. Determine what Sebas should do next.\",\"activeForm\":\"re-focusing on QR testing\",\"owner\":\"agent\"},{\"id\":9,\"subject\":\"Fix Mercado Pago QR webhook product id mapping\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order item external_code as fallback product id so paid QR orders persist in Ballbox.\",\"activeForm\":\"fixing QR webhook mapping\",\"owner\":\"agent\"},{\"id\":10,\"subject\":\"Fix Mercado Pago QR webhook amount parsing\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order total_amount as string or number so approved QR orders persist.\",\"activeForm\":\"fixing QR amount parsing\",\"owner\":\"agent\"},{\"id\":11,\"subject\":\"Reprioritize Ballbox work away from OPS-2 and machine-dependent payments\",\"status\":\"in_progress\",\"description\":\"Skip OPS-2 and machine-dependent payment work for now. Find and run as many non-machine Ballbox tasks in parallel as possible from this machine.\",\"activeForm\":\"reprioritizing Ballbox work\",\"owner\":\"agent\"},{\"id\":12,\"subject\":\"BB-003 implement portal offline slice 1\",\"status\":\"in_progress\",\"description\":\"Harden portal service worker with explicit offline fallback and bounded caches.\",\"activeForm\":\"implementing portal offline slice\",\"owner\":\"agent\"},{\"id\":13,\"subject\":\"BB-002 prepare Ballbox public offline slice 1 for implementation\",\"status\":\"completed\",\"description\":\"Prepare exact implementation-ready slice for Ballbox public offline shell and visited-page caching.\",\"activeForm\":\"preparing Ballbox offline slice\",\"owner\":\"agent\"},{\"id\":14,\"subject\":\"BB-004 prepare FreshRSS local trial setup\",\"status\":\"in_progress\",\"description\":\"Prepare or implement a local FreshRSS trial setup path for offline-friendly feed evaluation.\",\"activeForm\":\"preparing FreshRSS trial\",\"owner\":\"agent\"},{\"id\":15,\"subject\":\"Build minimal rss-offline reader\",\"status\":\"in_progress\",\"description\":\"Replace FreshRSS as primary offline reader path with a minimal local feed reader focused on date-sorted entries and preloaded real blog links, served at /rss-offline/.\",\"activeForm\":\"building rss-offline\",\"owner\":\"agent\"},{\"id\":16,\"subject\":\"Make rss-offline local copies default and precached\",\"status\":\"completed\",\"description\":\"Update rss-offline so primary click opens local copies and service worker precaches generated local article pages for offline use without manual first visit.\",\"activeForm\":\"hardening rss-offline offline behavior\",\"owner\":\"agent\"}],\"nextId\":17},\"isError\":false,\"timestamp\":1778836245256}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:522:{\"type\":\"message\",\"id\":\"c2f0663b\",\"parentId\":\"c86c612e\",\"timestamp\":\"2026-05-15T09:11:02.375Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"Hecho.\\n\\nCambios:\\n1. `rss-offline` ahora abre por default la **copia local**\\n- click principal = copia local\\n- link secundario = blog real\\n\\n2. service worker endurecido\\n- ahora **precacha las copias locales generadas**\\n- no hace falta abrir cada artículo una vez para que quede offline\\n\\nEstado nuevo:\\n- versión SW:\\n  - `rss-offline-v2`\\n- copias locales precargadas ahora:\\n  - 35 artículos\\n\\nQué probar:\\n1. abrir:\\n- https://ballbox-first.emperor-ratio.ts.net/rss-offline/\\n\\n2. esperar un poco a que cargue normal\\n\\n3. cortar conexión\\n\\n4. volver a entrar a:\\n- la lista\\n- uno o dos artículos locales\\n\\nAhora sí deberían abrir offline sin visita previa manual a cada uno.\",\"textSignature\":\"{\\\"v\\\":1,\\\"id\\\":\\\"msg_0532075662ec7a6e016a06e31cc81c8190bb5c6769697e1662\\\",\\\"phase\\\":\\\"final_answer\\\"}\"}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":1376,\"output\":176,\"cacheRead\":218624,\"cacheWrite\":0,\"totalTokens\":220176,\"cost\":{\"input\":0.0034400000000000003,\"output\":0.00264,\"cacheRead\":0.054655999999999996,\"cacheWrite\":0,\"total\":0.060736}},\"stopReason\":\"stop\",\"timestamp\":1778836245270,\"responseId\":\"resp_0532075662ec7a6e016a06e315fbc88190b3e6fe9087f6cea6\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:524:{\"type\":\"message\",\"id\":\"8ef0ea60\",\"parentId\":\"976dec89\",\"timestamp\":\"2026-05-15T09:39:22.245Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"**Considering service worker improvements**\\n\\nI need to keep my answer concise. I might suggest increasing the precache timing since the service worker might not wait long enough before going offline. It could be beneficial to show synchronization status on the index. The user asked if there's another way to improve, so I think a quick improvement would be adding a visible warmup status and even prefetching local articles on load. Implementing a chunked warmup from page JavaScript using fetch sounds good but requires some modification in build.py.\",\"thinkingSignature\":\"{\\\"id\\\":\\\"rs_0532075662ec7a6e016a06e9c2fac48190bc113be2ec0605d3\\\",\\\"type\\\":\\\"reasoning\\\",\\\"encrypted_content\\\":\\\"gAAAAABqBunJPbOEJafhn3cuEd0cSgyDuCs3qv9x5eZ3nZquup9ar13GaoBdccoH9I0KRiiETLv1svB7x0QUDLtRdYwIhho3ZjoRvgIe2c5FHlBrzTwGRDsbYSll7N1f-WKihpgl4KZzmJ-xUSLNmv4d0m6jqbhE60djAh_vsbcjF0iS4ReYv9_YXFAqz-pZB-2que0Oz2nCEObP7UtE1uAA6ye_Sam0ohKH73LNyKvs6hTgOPB9WU_zuEBOX7g3Oi5FYdiSsnuzFGwZX_WN0KB3Yg8knlDc9g02Qv9QsKq0rnoz5XQlyL1DprNhrYSoD2Dzz1jGJxl3pI-Mq33Ja0zy66yyaIvCqkhHk0_YTEbYObrPqnxdwAitceGjVbkxPFHccZhCUj5LDNs7Xttq9PNxB4Yv8a0BIIC_P5lVW8DFcfxAXPi_G-EoAahqRbZLHJRsar8QQEr4kml11g6Od2WWbDEF-sRP0IVMVhwq0zT42KAxD_bftLlJmagUYNUJFMnxCklBDM2q2z31OIH0gg98CdTMWq9OF2r60ShpILo7JMitfU42WrZjc12dqTFbPlNjqKjpqREKnqA_M3xhRPXHQl4EJcp9yMXTPH-P14Bz9DvYnuk_GjtZD4Cni67LcBwaH_9RWwYvsJn2FsCt4RaFaiUOEnZB5YG5B7TK4SFxJipu-PdGhPnqiNWsvG9wVSwA5qt5a7C6QNJau1DBZJh32ZMxYbBjpf1L5kvfya6Es20YIvIr7eMovGoFM1qT8BmpvgbkXoCFqPJu58ZghLQo2PtRXBa2ME6dlARoOkzGGNwaQ0rGm8LNS6RaUUSv1yJsS0sLVMtCAO5WHm3-BlbGWBs9o6V_kAjssArmrpXz_ZEX7pxciFEhEcvsqQQRpOsPBngXkYqbUx4xz9OMwyLF0KQpVsBIy1gw0ovCM5mS5uBLjHZGiPeQDhLTmeny1UteZke4gCzXZgHIbmujtKXQK9d2AW9Zv5xqafHiezHTAIKj77Lt9sYtzbr_D1AIzFBAm9Bx9QRQyHPTZcuQG9lun6W80eE7RoUrbu4fHBFyuwl4o43az23MG0bCr_3rlYKjngcX69MCa6dX7f-Bt7VW801wQTIPe2JZhREU_GNG76X3gUWwE4RJP2GdTp7jgISJDEMj6QxUgM7Tb9-QCmwjUtxTazYbYqYo2P750xvbT47WNF-xTmF-u5jKFJy2ENsTdEIzDreRXys3QkOvXCRvT5bLvp9APdxpuO_e8iP52AvtbjlU4htMR2g_stG2_ifCX9KKaQGKtQjgWXy5ymM3yI_pMMAkspjChUL-qfLfo-Mx2FEi7Ax1LnlaocpiybTaRIEcx3_pQW4JS1ZRV2XyEPqGaiu3MD84-zU-4U6peDg4KzzCL5xEDkGJYNkDdQqP1vj9oo-jqcXzES5USUgT2c4kct80F7gaf3XB1qLuezotJLWtEUttsJtI50xa_lVgbsd0G5V0hwcyMIj7Zfbb6lcvXHOW90G_IJmvz2Zdfq2I_QZPM_GlmN_Y8boINiLwG2yx-udTPX7XhfEl5R5zcTVfkHwM8P9DDlPG7DwalDKYjzcJT4xevyDkeuj3NcJ8tuNf8oPzi1SqN3wlxb28aQmAQmCiQePr1n0pD455f3rRGHaLFkCKa4r18MO8qLah8imeukzwxMg_xn3qfqQqudfCL2b2-eAjpJuWYm-AwGsSc7FOqnx0McXAllEt0aIz_cnlJeuxHaaYb8EnZE1CBVbkZRs3NoxTf8H1_xCKF43XVyzdSKR6JprEbO6_pMt8DUSZRIBcw6NvYxLiSVE_n7IAOpjwWRXwJa4Pd5HfPq-HH_B80dBaxOC-Na4W3kupNvmM0Fc81sLEnj8nWcUuoreiAcAPzaY8CW8b9bZEvK0svNpm5HcCTJgFmkJppqmVNC6ExMvVY45Fk2Ie_XEypticY9790S6LEzlR0kdM6p8voKs4YxaZ5DeLGkkCZuzCGm-gM08D2_3YJI0Q0StmZv9y_7khv402Yn7wQ8aUA0HyENgjBkrVu4LkJ1ZTO38WGGOgwzLdjYQr4hGcjpLeir8FKRWBF5uTONdeppTQpJ2Unkbdni0KjF_-8NAlUJWpjPYswXtQaEyeayGAs7njVU9VaBxawFleFpdTXaL0TKcMPI1pRRBvoDM6PZHYmGrxr3Q708Le\\\",\\\"summary\\\":[{\\\"type\\\":\\\"summary_text\\\",\\\"text\\\":\\\"**Considering service worker improvements**\\\\n\\\\nI need to keep my answer concise. I might suggest increasing the precache timing since the service worker might not wait long enough before going offline. It could be beneficial to show synchronization status on the index. The user asked if there's another way to improve, so I think a quick improvement would be adding a visible warmup status and even prefetching local articles on load. Implementing a chunked warmup from page JavaScript using fetch sounds good but requires some modification in build.py.\\\"}]}\"},{\"type\":\"toolCall\",\"id\":\"call_25RIrmwuuQ5yEJPNOsTAqxAN|fc_0532075662ec7a6e016a06e9c95554819085c134f206e462f7\",\"name\":\"todo\",\"arguments\":{\"action\":\"create\",\"subject\":\"Improve rss-offline local article warmup hit rate\",\"description\":\"Add client-side warmup fetch for local article copies so more articles are available offline without first opening them manually.\",\"activeForm\":\"improving rss-offline warmup\",\"status\":\"in_progress\",\"owner\":\"agent\"}}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":220175,\"output\":269,\"cacheRead\":0,\"cacheWrite\":0,\"totalTokens\":220444,\"cost\":{\"input\":0.5504375,\"output\":0.004035,\"cacheRead\":0,\"cacheWrite\":0,\"total\":0.5544725}},\"stopReason\":\"toolUse\",\"timestamp\":1778837951013,\"responseId\":\"resp_0532075662ec7a6e016a06e9bfa2248190ad51e1b589eec53d\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:525:{\"type\":\"message\",\"id\":\"35f31277\",\"parentId\":\"8ef0ea60\",\"timestamp\":\"2026-05-15T09:39:22.246Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_25RIrmwuuQ5yEJPNOsTAqxAN|fc_0532075662ec7a6e016a06e9c95554819085c134f206e462f7\",\"toolName\":\"todo\",\"content\":[{\"type\":\"text\",\"text\":\"Created #17: Improve rss-offline local article warmup hit rate (pending)\"}],\"details\":{\"action\":\"create\",\"params\":{\"action\":\"create\",\"subject\":\"Improve rss-offline local article warmup hit rate\",\"description\":\"Add client-side warmup fetch for local article copies so more articles are available offline without first opening them manually.\",\"activeForm\":\"improving rss-offline warmup\",\"status\":\"in_progress\",\"owner\":\"agent\"},\"tasks\":[{\"id\":1,\"subject\":\"BB-002 audit gomakethings patterns for Ballbox\",\"status\":\"completed\",\"description\":\"Research concrete frontend/offline/navigation patterns from gomakethings.com and related posts. Goal: extract practices applicable to Ballbox.\",\"activeForm\":\"researching gomakethings patterns\",\"owner\":\"agent\"},{\"id\":2,\"subject\":\"BB-003 inventory nginx-served surfaces for offline/navigation improvements\",\"status\":\"completed\",\"description\":\"Inventory current nginx-served web surfaces on ballbox-first and classify candidates for static caching, offline navigation, and resilience improvements.\",\"activeForm\":\"inventorying nginx surfaces\",\"owner\":\"agent\"},{\"id\":3,\"subject\":\"BB-004 define self-hosted RSS/offline reader product bar\",\"status\":\"completed\",\"description\":\"Define the minimal feature/product bar for a self-hosted feed reader that supports offline reading and beats the current app.\",\"activeForm\":\"defining feed reader product bar\",\"owner\":\"agent\"},{\"id\":4,\"subject\":\"BB-002 prepare reviewable Ballbox offline prototype plan\",\"status\":\"completed\",\"description\":\"Turn BB-002 research into concrete reviewable coding/testing slices for Ballbox.\",\"activeForm\":\"preparing Ballbox review slices\",\"owner\":\"agent\"},{\"id\":5,\"subject\":\"BB-003 prepare reviewable Pi web offline rollout plan\",\"status\":\"completed\",\"description\":\"Turn BB-003 research into concrete reviewable coding/testing slices for nginx-served surfaces.\",\"activeForm\":\"preparing Pi web review slices\",\"owner\":\"agent\"},{\"id\":6,\"subject\":\"BB-004 prepare OSS RSS reader shortlist and review plan\",\"status\":\"completed\",\"description\":\"Turn BB-004 research into concrete self-hosted OSS options and a test plan. No paid options.\",\"activeForm\":\"preparing RSS shortlist\",\"owner\":\"agent\"},{\"id\":7,\"subject\":\"Make copyparty file URLs more direct and preserve old /files/home links\",\"status\":\"completed\",\"description\":\"Change copyparty root mapping so /home/sebas content lives directly under /files/, and add nginx redirects for legacy /files/home/... URLs.\",\"activeForm\":\"updating copyparty paths\",\"owner\":\"agent\"},{\"id\":8,\"subject\":\"Re-focus on Ballbox QR testing\",\"status\":\"completed\",\"description\":\"Pause backlog follow-up work and switch active focus to QR testing inside Ballbox. Determine what Sebas should do next.\",\"activeForm\":\"re-focusing on QR testing\",\"owner\":\"agent\"},{\"id\":9,\"subject\":\"Fix Mercado Pago QR webhook product id mapping\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order item external_code as fallback product id so paid QR orders persist in Ballbox.\",\"activeForm\":\"fixing QR webhook mapping\",\"owner\":\"agent\"},{\"id\":10,\"subject\":\"Fix Mercado Pago QR webhook amount parsing\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order total_amount as string or number so approved QR orders persist.\",\"activeForm\":\"fixing QR amount parsing\",\"owner\":\"agent\"},{\"id\":11,\"subject\":\"Reprioritize Ballbox work away from OPS-2 and machine-dependent payments\",\"status\":\"in_progress\",\"description\":\"Skip OPS-2 and machine-dependent payment work for now. Find and run as many non-machine Ballbox tasks in parallel as possible from this machine.\",\"activeForm\":\"reprioritizing Ballbox work\",\"owner\":\"agent\"},{\"id\":12,\"subject\":\"BB-003 implement portal offline slice 1\",\"status\":\"in_progress\",\"description\":\"Harden portal service worker with explicit offline fallback and bounded caches.\",\"activeForm\":\"implementing portal offline slice\",\"owner\":\"agent\"},{\"id\":13,\"subject\":\"BB-002 prepare Ballbox public offline slice 1 for implementation\",\"status\":\"completed\",\"description\":\"Prepare exact implementation-ready slice for Ballbox public offline shell and visited-page caching.\",\"activeForm\":\"preparing Ballbox offline slice\",\"owner\":\"agent\"},{\"id\":14,\"subject\":\"BB-004 prepare FreshRSS local trial setup\",\"status\":\"in_progress\",\"description\":\"Prepare or implement a local FreshRSS trial setup path for offline-friendly feed evaluation.\",\"activeForm\":\"preparing FreshRSS trial\",\"owner\":\"agent\"},{\"id\":15,\"subject\":\"Build minimal rss-offline reader\",\"status\":\"in_progress\",\"description\":\"Replace FreshRSS as primary offline reader path with a minimal local feed reader focused on date-sorted entries and preloaded real blog links, served at /rss-offline/.\",\"activeForm\":\"building rss-offline\",\"owner\":\"agent\"},{\"id\":16,\"subject\":\"Make rss-offline local copies default and precached\",\"status\":\"completed\",\"description\":\"Update rss-offline so primary click opens local copies and service worker precaches generated local article pages for offline use without manual first visit.\",\"activeForm\":\"hardening rss-offline offline behavior\",\"owner\":\"agent\"},{\"id\":17,\"subject\":\"Improve rss-offline local article warmup hit rate\",\"status\":\"pending\",\"description\":\"Add client-side warmup fetch for local article copies so more articles are available offline without first opening them manually.\",\"activeForm\":\"improving rss-offline warmup\",\"owner\":\"agent\"}],\"nextId\":18},\"isError\":false,\"timestamp\":1778837962246}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:527:{\"type\":\"message\",\"id\":\"d6d6d369\",\"parentId\":\"7a358f30\",\"timestamp\":\"2026-05-15T09:39:28.747Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_bebenDTT5lviabTT75AcUba4|fc_0532075662ec7a6e016a06e9cf3d24819090df420e75907f89\",\"toolName\":\"todo\",\"content\":[{\"type\":\"text\",\"text\":\"Updated #17 (pending → in_progress)\"}],\"details\":{\"action\":\"update\",\"params\":{\"action\":\"update\",\"id\":17,\"status\":\"in_progress\"},\"tasks\":[{\"id\":1,\"subject\":\"BB-002 audit gomakethings patterns for Ballbox\",\"status\":\"completed\",\"description\":\"Research concrete frontend/offline/navigation patterns from gomakethings.com and related posts. Goal: extract practices applicable to Ballbox.\",\"activeForm\":\"researching gomakethings patterns\",\"owner\":\"agent\"},{\"id\":2,\"subject\":\"BB-003 inventory nginx-served surfaces for offline/navigation improvements\",\"status\":\"completed\",\"description\":\"Inventory current nginx-served web surfaces on ballbox-first and classify candidates for static caching, offline navigation, and resilience improvements.\",\"activeForm\":\"inventorying nginx surfaces\",\"owner\":\"agent\"},{\"id\":3,\"subject\":\"BB-004 define self-hosted RSS/offline reader product bar\",\"status\":\"completed\",\"description\":\"Define the minimal feature/product bar for a self-hosted feed reader that supports offline reading and beats the current app.\",\"activeForm\":\"defining feed reader product bar\",\"owner\":\"agent\"},{\"id\":4,\"subject\":\"BB-002 prepare reviewable Ballbox offline prototype plan\",\"status\":\"completed\",\"description\":\"Turn BB-002 research into concrete reviewable coding/testing slices for Ballbox.\",\"activeForm\":\"preparing Ballbox review slices\",\"owner\":\"agent\"},{\"id\":5,\"subject\":\"BB-003 prepare reviewable Pi web offline rollout plan\",\"status\":\"completed\",\"description\":\"Turn BB-003 research into concrete reviewable coding/testing slices for nginx-served surfaces.\",\"activeForm\":\"preparing Pi web review slices\",\"owner\":\"agent\"},{\"id\":6,\"subject\":\"BB-004 prepare OSS RSS reader shortlist and review plan\",\"status\":\"completed\",\"description\":\"Turn BB-004 research into concrete self-hosted OSS options and a test plan. No paid options.\",\"activeForm\":\"preparing RSS shortlist\",\"owner\":\"agent\"},{\"id\":7,\"subject\":\"Make copyparty file URLs more direct and preserve old /files/home links\",\"status\":\"completed\",\"description\":\"Change copyparty root mapping so /home/sebas content lives directly under /files/, and add nginx redirects for legacy /files/home/... URLs.\",\"activeForm\":\"updating copyparty paths\",\"owner\":\"agent\"},{\"id\":8,\"subject\":\"Re-focus on Ballbox QR testing\",\"status\":\"completed\",\"description\":\"Pause backlog follow-up work and switch active focus to QR testing inside Ballbox. Determine what Sebas should do next.\",\"activeForm\":\"re-focusing on QR testing\",\"owner\":\"agent\"},{\"id\":9,\"subject\":\"Fix Mercado Pago QR webhook product id mapping\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order item external_code as fallback product id so paid QR orders persist in Ballbox.\",\"activeForm\":\"fixing QR webhook mapping\",\"owner\":\"agent\"},{\"id\":10,\"subject\":\"Fix Mercado Pago QR webhook amount parsing\",\"status\":\"completed\",\"description\":\"Accept Mercado Pago order total_amount as string or number so approved QR orders persist.\",\"activeForm\":\"fixing QR amount parsing\",\"owner\":\"agent\"},{\"id\":11,\"subject\":\"Reprioritize Ballbox work away from OPS-2 and machine-dependent payments\",\"status\":\"in_progress\",\"description\":\"Skip OPS-2 and machine-dependent payment work for now. Find and run as many non-machine Ballbox tasks in parallel as possible from this machine.\",\"activeForm\":\"reprioritizing Ballbox work\",\"owner\":\"agent\"},{\"id\":12,\"subject\":\"BB-003 implement portal offline slice 1\",\"status\":\"in_progress\",\"description\":\"Harden portal service worker with explicit offline fallback and bounded caches.\",\"activeForm\":\"implementing portal offline slice\",\"owner\":\"agent\"},{\"id\":13,\"subject\":\"BB-002 prepare Ballbox public offline slice 1 for implementation\",\"status\":\"completed\",\"description\":\"Prepare exact implementation-ready slice for Ballbox public offline shell and visited-page caching.\",\"activeForm\":\"preparing Ballbox offline slice\",\"owner\":\"agent\"},{\"id\":14,\"subject\":\"BB-004 prepare FreshRSS local trial setup\",\"status\":\"in_progress\",\"description\":\"Prepare or implement a local FreshRSS trial setup path for offline-friendly feed evaluation.\",\"activeForm\":\"preparing FreshRSS trial\",\"owner\":\"agent\"},{\"id\":15,\"subject\":\"Build minimal rss-offline reader\",\"status\":\"in_progress\",\"description\":\"Replace FreshRSS as primary offline reader path with a minimal local feed reader focused on date-sorted entries and preloaded real blog links, served at /rss-offline/.\",\"activeForm\":\"building rss-offline\",\"owner\":\"agent\"},{\"id\":16,\"subject\":\"Make rss-offline local copies default and precached\",\"status\":\"completed\",\"description\":\"Update rss-offline so primary click opens local copies and service worker precaches generated local article pages for offline use without manual first visit.\",\"activeForm\":\"hardening rss-offline offline behavior\",\"owner\":\"agent\"},{\"id\":17,\"subject\":\"Improve rss-offline local article warmup hit rate\",\"status\":\"in_progress\",\"description\":\"Add client-side warmup fetch for local article copies so more articles are available offline without first opening them manually.\",\"activeForm\":\"improving rss-offline warmup\",\"owner\":\"agent\"}],\"nextId\":18},\"isError\":false,\"timestamp\":1778837968747}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:528:{\"type\":\"message\",\"id\":\"5406553d\",\"parentId\":\"d6d6d369\",\"timestamp\":\"2026-05-15T09:39:52.762Z\",\"message\":{\"role\":\"assistant\",\"content\":[{\"type\":\"toolCall\",\"id\":\"call_nWaUIV9VL4H69l2pZPtbI2V7|fc_0532075662ec7a6e016a06e9d427488190897ebf6c01e95cc4\",\"name\":\"edit\",\"arguments\":{\"path\":\"/home/sebas/work/projects/rss-offline/build.py\",\"edits\":[{\"oldText\":\"    index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});</script></main></body></html>''')\\n\",\"newText\":\"    local_paths = [entry['local_path'] for entry in archived]\\n    index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\\n\"}]}}],\"api\":\"openai-codex-responses\",\"provider\":\"openai-codex\",\"model\":\"gpt-5.4\",\"usage\":{\"input\":1380,\"output\":1074,\"cacheRead\":219136,\"cacheWrite\":0,\"totalTokens\":221590,\"cost\":{\"input\":0.0034500000000000004,\"output\":0.01611,\"cacheRead\":0.054784,\"cacheWrite\":0,\"total\":0.074344}},\"stopReason\":\"toolUse\",\"timestamp\":1778837968757,\"responseId\":\"resp_0532075662ec7a6e016a06e9d15fb8819085533c8e6c1c4920\"}}\n/home/sebas/pi-config/sessions/--home-sebas--/2026-05-12T11-39-30-483Z_019e1bfc-2732-7351-b63a-429fd2c3a363.jsonl:529:{\"type\":\"message\",\"id\":\"0669fc39\",\"parentId\":\"5406553d\",\"timestamp\":\"2026-05-15T09:39:52.865Z\",\"message\":{\"role\":\"toolResult\",\"toolCallId\":\"call_nWaUIV9VL4H69l2pZPtbI2V7|fc_0532075662ec7a6e016a06e9d427488190897ebf6c01e95cc4\",\"toolName\":\"edit\",\"content\":[{\"type\":\"text\",\"text\":\"Successfully replaced 1 block(s) in /home/sebas/work/projects/rss-offline/build.py.\"}],\"details\":{\"diff\":\"     ...\\n 150     cards = []\\n 151     for entry in archived:\\n 152         cards.append(f'''<article class=\\\"item\\\"><h2><a href=\\\"{escape(entry['local_path'])}\\\">{escape(entry['title'])}</a></h2><p class=\\\"meta\\\">{escape(entry['feed'])} · {escape(entry['date_label'])}</p><p class=\\\"actions\\\"><strong>copia local</strong> · <a href=\\\"{escape(entry['link'])}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">blog real</a></p></article>''')\\n 153 \\n-154     index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});</script></main></body></html>''')\\n+154     local_paths = [entry['local_path'] for entry in archived]\\n+155     index.write_text(f'''<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><meta name=\\\"theme-color\\\" content=\\\"#07111f\\\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta,.actions{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}</style></head><body><main><div class=\\\"top\\\"><div><h1>rss-offline</h1><p class=\\\"muted\\\">Artículos por fecha con copia local y link real precargado.</p></div><p class=\\\"muted\\\">{len(archived)} artículos · últimos {DAYS} días</p></div>{''.join(cards)}<script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}const seen={{}};document.querySelectorAll('a[href]').forEach((a)=>{{const href=a.href;if(!href||seen[href])return;if(href.startsWith(window.location.origin+'/rss-offline/'))return;seen[href]=true;const l=document.createElement('link');l.rel='prefetch';l.href=href;l.as='document';l.crossOrigin='anonymous';document.head.appendChild(l);}});window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\\n 155 \\n 156     offline.write_text('<!doctype html><html lang=\\\"es\\\"><head><meta charset=\\\"utf-8\\\"><meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"><title>rss-offline sin conexión</title><style>body{font-family:system-ui,sans-serif;background:#07111f;color:#fff;display:grid;place-items:center;min-height:100vh;padding:24px;margin:0}main{max-width:520px;padding:24px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.05)}a{color:#8dc3ff}</style></head><body><main><h1>Sin conexión</h1><p>La lista y copias locales visitadas deberían seguir disponibles.</p><p><a href=\\\"/rss-offline/\\\">Volver a rss-offline</a></p></main></body></html>')\\n 157 \\n 158     precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + [entry['local_path'] for entry in archived]\\n     ...\",\"firstChangedLine\":154},\"isError\":false,\"timestamp\":1778837992863}}","truncated":true,"truncatedBy":"bytes","totalLines":50,"totalBytes":297603,"outputLines":11,"outputBytes":48809,"lastLinePartial":false,"firstLineExceedsLimit":false,"maxLines":2000,"maxBytes":51200},"fullOutputPath":"/tmp/pi-bash-ec64b4c83565caf5.log"},"isError":false,"timestamp":1780214934471}}
{"type":"message","id":"37c46d25","parentId":"cab371c7","timestamp":"2026-05-31T08:08:57.707Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bec97785081918baafc7e0492dbe0\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG-yZQqj_G29A-VS8-4y6AkPaOpphStKqVzhKJzw3Xw6bmXrmIdLeX4gHtykdyhE1d4hkt8mXGM_2ITBvijKvlWpXPBHR5ITZa5s6cH2AjZqPcbOvoFJvKUyszR9AG9sqOzl-dec1PrtpWf0gqiLxcA46swLw5mcP2jZhxGL3LtY8VXDYgCxvKClT1pSiRb8ABY3dhP2ZK_Ge0cRD8Mm3eu3ioOpCHtC_1Hwwnj3RDkCzoCMg0kG4xdwxy1KXwv7mlkktuuxXOErX3GaAVLM29ZUnR6lb_r6MzQBvajBeczBuGkZmj_vkyMUrGXxTa6SuYKqfWdQ6BUUJx37-ijfOKkoZ2mTnTGOS1tqGBxHKL2U-geQjFzGejZ2rT8Ki9E2vSzgF-dKEPGKDSzGsvmRvuJIKF9T8QdA90YVHhw_ApbZ_Kia36bBdQaY_o26AFVhgyHCCdM63xmUQRBAsi8wzlZdBpnp65xBdPLPy9Llf5h1Cz-aqnDhroKwNmURPzRtiH9sxaN0Si-iHbZwbOJXxYp8djh-biQx1a7OVx-gMwAdUv6KR9dww0aHzIr4spRRd6szY3v4o08OZDMWx88tdacQ3jR1m5oAlfN9bzHY-NyuoybjiItH2519iVrrmoA2_hm7r5wYEwe7t0hbdqYqIe03luiNLGqxNKmNypdjwECA-4kHtZNrRjH5vnhZxeMpSuRrPudTSfHAnfxEZOS3tOaSz5Ih8wv2f1g3xZuCc8d-F3XrDfR8vvHYmxe04bqS6-FP5xdUf5_f6OPlgnU4lFc2x7W9g1nvGqHIU87bueVkKflCaSt5T7bTNfdDEhIkygdVvLvZ7du1hJu0snBI4S3hUyW_L1zjIWgm_Ea8D4LtNAc5iWJ943hmVnl1V9kB3B_BhR2M9XAlomNS6AT8G-MPdJHuCD2vpYtihaNvHP-qwTkpUZayRjlVqtyqAj0Zu-3Hx\",\"summary\":[]}"},{"type":"toolCall","id":"call_sUucpyffbBqvPJxo6uvLqu5p|fc_0f029265420fe376016a1bec99a6048191884739b0f4a44b73","name":"read","arguments":{"path":"/home/sebas/work/projects/rss-offline/build.py","offset":1,"limit":260}},{"type":"toolCall","id":"call_K4zwIrNEY4iPp2vmd4dlBDCa|fc_0f029265420fe376016a1bec99a61481919ed5b3659322e48d","name":"bash","arguments":{"command":"cd /home/sebas/work/projects/rss-offline && ls -1 && find . -maxdepth 2 -type f | sed 's#^./##' | sort | head -100","timeout":10}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":19906,"output":119,"cacheRead":0,"cacheWrite":0,"totalTokens":20025,"cost":{"input":0.049765000000000004,"output":0.0017850000000000001,"cacheRead":0,"cacheWrite":0,"total":0.051550000000000006}},"stopReason":"toolUse","timestamp":1780214934472,"responseId":"resp_0f029265420fe376016a1bec96a82081919c300d0c2197b4eb"}}
{"type":"message","id":"0e4bd41e","parentId":"37c46d25","timestamp":"2026-05-31T08:08:57.720Z","message":{"role":"toolResult","toolCallId":"call_sUucpyffbBqvPJxo6uvLqu5p|fc_0f029265420fe376016a1bec99a6048191884739b0f4a44b73","toolName":"read","content":[{"type":"text","text":"#!/usr/bin/env python3\nimport email.utils\nimport hashlib\nimport json\nimport os\nimport re\nimport subprocess\nimport time\nimport xml.etree.ElementTree as ET\nfrom datetime import datetime, timedelta, timezone\nfrom html import escape\nfrom pathlib import Path\nfrom urllib.parse import urljoin, urlparse\n\nimport requests\nfrom bs4 import BeautifulSoup\n\nBASE_DIR = Path(__file__).resolve().parent\nSOURCES_PATH = BASE_DIR / 'feed-sources.json'\nDATA_DIR = BASE_DIR / 'data'\nOUTPUT_DIR = Path('/var/www/rss-offline')\nMAX_ENTRIES = int(os.environ.get('RSS_OFFLINE_MAX_ENTRIES', '250'))\nDAYS = int(os.environ.get('RSS_OFFLINE_DAYS', '14'))\nTIMEOUT = 20\nHEADERS = {'user-agent': 'Mozilla/5.0 (rss-offline builder)'}\nDEFAULT_CAPTURE_MODE = os.environ.get('RSS_OFFLINE_CAPTURE_MODE', 'rendered').strip().lower() or 'rendered'\nRENDER_BUDGET_MS = int(os.environ.get('RSS_OFFLINE_RENDER_BUDGET_MS', '8000'))\nMAX_RENDERED_ENTRIES = int(os.environ.get('RSS_OFFLINE_MAX_RENDERED_ENTRIES', '8'))\nMAX_ASSETS_PER_PAGE = int(os.environ.get('RSS_OFFLINE_MAX_ASSETS_PER_PAGE', '24'))\nMAX_ASSET_BYTES = int(os.environ.get('RSS_OFFLINE_MAX_ASSET_BYTES', '1500000'))\nALLOWED_ASSET_EXTENSIONS = ('.css', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico')\nCHROMIUM_BIN = os.environ.get('RSS_OFFLINE_CHROMIUM_BIN', '/usr/bin/chromium')\nPDF_DIRNAME = os.environ.get('RSS_OFFLINE_PDF_DIRNAME', 'pdf')\nPDF_MAX_ARTICLES = int(os.environ.get('RSS_OFFLINE_PDF_MAX_ARTICLES', '8'))\nPDF_BASENAME = os.environ.get('RSS_OFFLINE_PDF_BASENAME', 'rss-offline')\nPDF_PAGE_TIMEOUT = int(os.environ.get('RSS_OFFLINE_PDF_PAGE_TIMEOUT', '90'))\nPDF_MAX_SOURCE_BYTES = int(os.environ.get('RSS_OFFLINE_PDF_MAX_SOURCE_BYTES', '2500000'))\n\n\ndef fetch(url: str) -> requests.Response:\n    response = requests.get(url, timeout=TIMEOUT, headers=HEADERS)\n    response.raise_for_status()\n    return response\n\n\ndef text(node, path_options):\n    for path in path_options:\n        found = node.find(path)\n        if found is not None and found.text and found.text.strip():\n            return found.text.strip()\n    return ''\n\n\ndef entry_links(item):\n    for tag in ['link', '{http://www.w3.org/2005/Atom}link']:\n        for node in item.findall(tag):\n            href = node.get('href') or (node.text or '').strip()\n            if href:\n                rel = node.get('rel', 'alternate')\n                if rel in ('alternate', ''):\n                    return href\n    return ''\n\n\ndef parse_timestamp(raw: str) -> int:\n    raw = (raw or '').strip()\n    if not raw:\n        return 0\n    try:\n        return int(email.utils.parsedate_to_datetime(raw).timestamp())\n    except Exception:\n        pass\n    try:\n        return int(datetime.fromisoformat(raw.replace('Z', '+00:00')).timestamp())\n    except Exception:\n        return 0\n\n\ndef slug(url: str) -> str:\n    return hashlib.sha256(url.encode()).hexdigest()[:16]\n\n\ndef asset_rel_path(entry_key: str, asset_url: str, content_type: str = '') -> str:\n    parsed = urlparse(asset_url)\n    raw = (parsed.path or '').lower()\n    ext = Path(raw).suffix\n    if not ext:\n        if 'css' in content_type:\n            ext = '.css'\n        elif 'javascript' in content_type:\n            ext = '.js'\n        elif 'svg' in content_type:\n            ext = '.svg'\n        elif 'png' in content_type:\n            ext = '.png'\n        elif 'jpeg' in content_type:\n            ext = '.jpg'\n        elif 'webp' in content_type:\n            ext = '.webp'\n        elif 'gif' in content_type:\n            ext = '.gif'\n        elif 'woff2' in content_type:\n            ext = '.woff2'\n        elif 'woff' in content_type:\n            ext = '.woff'\n        elif 'html' in content_type:\n            ext = '.html'\n        else:\n            ext = '.bin'\n    name = hashlib.sha256(asset_url.encode()).hexdigest()[:16] + ext\n    return f'assets/{entry_key}/{name}'\n\n\ndef fetch_asset(url: str) -> requests.Response:\n    response = requests.get(url, timeout=TIMEOUT, headers=HEADERS, stream=True)\n    response.raise_for_status()\n    return response\n\n\ndef should_keep_asset(node, parsed):\n    path_lower = parsed.path.lower()\n    if node.name == 'link' and 'stylesheet' in ' '.join(node.get('rel', [])).lower():\n        return True\n    if node.name != 'img':\n        return False\n    return any(path_lower.endswith(ext) for ext in ALLOWED_ASSET_EXTENSIONS)\n\n\ndef mirror_assets(entry, soup, entry_key: str):\n    page_origin = urlparse(entry['link']).netloc\n    mirrored = {}\n    total_bytes = 0\n    count = 0\n\n    def candidates():\n        for tag, attr in [('link', 'href'), ('img', 'src')]:\n            for node in soup.find_all(tag):\n                ref = node.get(attr)\n                if ref:\n                    yield node, attr, ref\n\n    for node, attr, ref in candidates():\n        if count >= MAX_ASSETS_PER_PAGE or total_bytes >= MAX_ASSET_BYTES:\n            break\n        asset_url = urljoin(entry['link'], ref)\n        parsed = urlparse(asset_url)\n        if parsed.scheme not in ('http', 'https'):\n            continue\n        if parsed.netloc != page_origin:\n            continue\n        if not should_keep_asset(node, parsed):\n            continue\n        try:\n            rel = mirrored.get(asset_url)\n            if rel is None:\n                res = fetch_asset(asset_url)\n                content_type = res.headers.get('content-type', '').lower()\n                rel = asset_rel_path(entry_key, asset_url, content_type)\n                data = b''\n                limit_hit = False\n                for chunk in res.iter_content(65536):\n                    if not chunk:\n                        continue\n                    data += chunk\n                    if len(data) + total_bytes > MAX_ASSET_BYTES:\n                        limit_hit = True\n                        break\n                res.close()\n                if limit_hit or not data:\n                    continue\n                out = OUTPUT_DIR / rel\n                out.parent.mkdir(parents=True, exist_ok=True)\n                out.write_bytes(data)\n                mirrored[asset_url] = rel\n                total_bytes += len(data)\n                count += 1\n            node[attr] = '/rss-offline/' + rel\n        except Exception:\n            continue\n\n\ndef render_page(url: str) -> str:\n    cmd = [\n        CHROMIUM_BIN,\n        '--headless=new',\n        '--disable-gpu',\n        '--no-sandbox',\n        '--run-all-compositor-stages-before-draw',\n        f'--virtual-time-budget={RENDER_BUDGET_MS}',\n        '--dump-dom',\n        url,\n    ]\n    result = subprocess.run(cmd, capture_output=True, text=True, timeout=max(TIMEOUT, (RENDER_BUDGET_MS // 1000) + 10), check=True)\n    html = result.stdout.strip()\n    if not html:\n        raise RuntimeError('empty rendered DOM')\n    return html\n\n\ndef capture_page(entry, rendered_allowed: bool):\n    preferred = (entry.get('capture_mode') or DEFAULT_CAPTURE_MODE).lower()\n    if preferred == 'rendered' and not rendered_allowed:\n        preferred = 'simple'\n    modes = [preferred] + [mode for mode in ('rendered', 'simple') if mode != preferred and (mode != 'rendered' or rendered_allowed)]\n    errors = []\n    for mode in modes:\n        try:\n            if mode == 'rendered':\n                return render_page(entry['link']), 'rendered'\n            if mode == 'simple':\n                return fetch(entry['link']).text, 'simple'\n        except Exception as e:\n            errors.append(f'{mode}:{e}')\n    raise RuntimeError(' ; '.join(errors))\n\n\ndef make_local_copy(entry, page_html: str, entry_key: str) -> str:\n    soup = BeautifulSoup(page_html, 'html.parser')\n\n    if soup.head is None:\n        head = soup.new_tag('head')\n        if soup.html is None:\n            html = soup.new_tag('html')\n            html.append(head)\n            body = soup.new_tag('body')\n            for child in list(soup.contents):\n                body.append(child.extract())\n            html.append(body)\n            soup.append(html)\n        else:\n            soup.html.insert(0, head)\n\n    if soup.head.find('meta', attrs={'charset': True}) is None:\n        soup.head.insert(0, soup.new_tag('meta', charset='utf-8'))\n    if soup.head.find('meta', attrs={'name': 'viewport'}) is None:\n        soup.head.append(soup.new_tag('meta', attrs={'name': 'viewport', 'content': 'width=device-width, initial-scale=1'}))\n\n    for base in soup.head.find_all('base'):\n        base.decompose()\n    soup.head.insert(0, soup.new_tag('base', href=entry['link']))\n\n    banner_css = (\n        'body{margin-top:72px!important}'\n        '.rss-offline-banner{position:fixed;top:0;left:0;right:0;z-index:2147483647;'\n        'background:#07111f;color:#fff;padding:12px 16px;font:14px/1.4 system-ui,sans-serif;'\n        'box-shadow:0 1px 0 rgba(255,255,255,.08)}'\n        '.rss-offline-banner a{color:#8dc3ff;text-decoration:none}'\n        '.rss-offline-banner strong{color:#fff}'\n        '.rss-offline-banner .muted{opacity:.78}'\n    )\n    style = soup.new_tag('style')\n    style.string = banner_css\n    soup.head.append(style)\n\n    mirror_assets(entry, soup, entry_key)\n\n    banner = BeautifulSoup(\n        f'<div class=\"rss-offline-banner\"><a href=\"/rss-offline/\">← rss-offline</a> · '\n        f'<a href=\"{escape(entry[\"link\"], quote=True)}\">abrir original</a> · '\n        f'<strong>{escape(entry[\"title\"])} [{escape(entry[\"captured_mode\"])}]</strong> · '\n\n[247 more lines in file. Use offset=261 to continue.]"}],"isError":false,"timestamp":1780214937720}}
{"type":"message","id":"b8cbde55","parentId":"0e4bd41e","timestamp":"2026-05-31T08:08:57.721Z","message":{"role":"toolResult","toolCallId":"call_K4zwIrNEY4iPp2vmd4dlBDCa|fc_0f029265420fe376016a1bec99a61481919ed5b3659322e48d","toolName":"bash","content":[{"type":"text","text":"build.py\ndata\nfeed-sources.json\n__pycache__\nbuild.py\nfeed-sources.json\n__pycache__/build.cpython-313.pyc\n"}],"isError":false,"timestamp":1780214937721}}
{"type":"message","id":"87103f9a","parentId":"b8cbde55","timestamp":"2026-05-31T08:08:59.772Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bec9ad0308191ab2d8112ce62b9ae\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG-yb3QaVFxQiwSQb0RLiGtkbRRlpmcCOvyII_OgoL-qP6fpeYmsmGQIx50AFKaOJD2vX6tyGka3TjfrYK-ntboRzojMN5jxghfIJZoBd9QjFvwgdbcoxx9qvIDrD6nFOQakFVmoKjsdiJbf7i6BSSMk7nf302cXTiDtjFiIWDI8krdOZksVfJYC8RbC47sJHGbCwaHml3hFsMbUkIdGjKEbRk28ahW9hRdbszw6HGGQ5hQahtbkrB2xJdKS2uGVcJ5mBFXmMWR1o9b3T5isGaf0aOyMkdZz9vCGf0JB5AeZ-Pp5MIMuf6lQ3QzK5rtSfrKqVGHWRhmiuOE3aiRnZswitjCO_-LqU_R48kuSXt0_zpq84tuqVxnrUVS4wfVtM2GPafclU85UpRX7cNVlDwsEcNVzl45AJMe772674Ym3eupakU16DHXXgbuI_DenE_q1lQEZq1_cY7tEObDn0ZxLbXkWPF4WBXLK4HKnqa9jMlPTkM-7sr7eQrXKfVHP8KyPrCGO7gGqUYNG53CDz9tw8W5NHpWWVkysbxCwfpXMFOUdEGtUqi3ZpcDdh_qBX9-qyZDIIcM9vsmes07gg_LnHdbCHC3NOjQaUMV6g_UDphOJXX5zrH7EaoaCBzR6behoEfDTgc1QqDYnL3yJPdsDu_-UtEcLhehWWs14smdWehlunecb4GODJ51L3_Ekwf_BupAJRA6ssmDx-bgtdKpJ3alA37dy9PhB5Kt_neTD8c8C7bKrZt-Bms0wAkHTNCHTgvVvy5eU3IF40uIHC5zanslGP3x96YMw3IcvV0yFG0pvT8Lo2Cvs0FD4iITT8z96Zp4sCreYOiYTauPjVznCgn70F0gX6yOeUo6RI68zWP3Nu6ZE179-liVxMWlLVWhdXnuG3d8-nV-ueRbZpwfI_QcTG6EVXjoKNwC5XudcuV7UcDy6c594CtNyrKIheljMB3e_UJeLHIbj2jpYOalZRMA==\",\"summary\":[]}"},{"type":"toolCall","id":"call_pyj02YyoeNzbea9fVhWUz43q|fc_0f029265420fe376016a1bec9b7fa081918dc25a3d0429cb7f","name":"read","arguments":{"path":"/home/sebas/work/projects/rss-offline/build.py","offset":261,"limit":220}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":17251,"output":48,"cacheRead":5120,"cacheWrite":0,"totalTokens":22419,"cost":{"input":0.043127500000000006,"output":0.00072,"cacheRead":0.0012799999999999999,"cacheWrite":0,"total":0.0451275}},"stopReason":"toolUse","timestamp":1780214937721,"responseId":"resp_0f029265420fe376016a1bec99c7cc8191847fc53d4ada5f38"}}
{"type":"message","id":"b237e020","parentId":"87103f9a","timestamp":"2026-05-31T08:08:59.778Z","message":{"role":"toolResult","toolCallId":"call_pyj02YyoeNzbea9fVhWUz43q|fc_0f029265420fe376016a1bec9b7fa081918dc25a3d0429cb7f","toolName":"read","content":[{"type":"text","text":"        f'<span class=\"muted\">{escape(entry[\"feed\"])} · {escape(entry[\"date_label\"])} </span></div>',\n        'html.parser'\n    )\n    body = soup.body or soup\n    body.insert(0, banner)\n\n    return '<!doctype html>\\n' + str(soup)\n\n\ndef load_sources():\n    sources = json.loads(SOURCES_PATH.read_text())\n    for source in sources:\n        source['captureMode'] = (source.get('captureMode') or DEFAULT_CAPTURE_MODE).lower()\n    return sources\n\n\ndef isolate_anchor_section(soup: BeautifulSoup, entry: dict):\n    parsed = urlparse(entry['link'])\n    fragment = (parsed.fragment or '').strip()\n    if not fragment:\n        return soup\n    target = soup.find(id=fragment)\n    if target is None:\n        target = soup.find(attrs={'name': fragment})\n    if target is None:\n        return soup\n    container = target\n    for _ in range(6):\n        parent = container.parent\n        if parent is None or not getattr(parent, 'name', None):\n            break\n        text_len = len(parent.get_text(' ', strip=True))\n        if 200 < text_len < 20000:\n            container = parent\n            break\n        container = parent\n    wrapper = BeautifulSoup('<!doctype html><html><head></head><body></body></html>', 'html.parser')\n    wrapper.body.append(BeautifulSoup(str(container), 'html.parser'))\n    return wrapper\n\n\ndef clean_pdf_soup(soup: BeautifulSoup):\n    selectors = [\n        'script', 'noscript', 'iframe', 'video', 'audio',\n        '[aria-modal=\"true\"]', '[role=\"dialog\"]',\n        '.modal', '.overlay', '.backdrop', '.drawer', '.popup',\n        '[class*=\"overlay\"]', '[class*=\"backdrop\"]',\n        '[style*=\"backdrop-filter\"]'\n    ]\n    for selector in selectors:\n        for tag in soup.select(selector):\n            tag.decompose()\n    for tag in soup.find_all(True):\n        style = (tag.get('style') or '').lower().replace(' ', '')\n        klass = ' '.join(tag.get('class', [])).lower()\n        if ('position:fixed' in style or 'position:sticky' in style) and (\n            'overlay' in klass or 'backdrop' in klass or 'modal' in klass or\n            'inset:0' in style or ('top:0' in style and 'left:0' in style and 'right:0' in style and 'bottom:0' in style)\n        ):\n            tag.decompose()\n    return soup\n\n\ndef make_pdf_printable_copy(entry: dict):\n    src_path = OUTPUT_DIR / f'article-{slug(entry[\"link\"])}.html'\n    pdf_dir = OUTPUT_DIR / PDF_DIRNAME\n    pdf_dir.mkdir(parents=True, exist_ok=True)\n    printable_path = pdf_dir / f'article-{slug(entry[\"link\"])}.print.html'\n    soup = BeautifulSoup(src_path.read_text(), 'html.parser')\n    soup = isolate_anchor_section(soup, entry)\n    soup = clean_pdf_soup(soup)\n    html = str(soup)\n    html = html.replace('/rss-offline/', 'file:///var/www/rss-offline/')\n    html = html.replace('position:fixed;top:0;left:0;right:0;', 'position:static;')\n    html = html.replace('body{margin-top:72px!important}', 'body{margin-top:0!important}')\n    html = html.replace('</head>', '<style>@page{size:auto;margin:12mm}html,body{background:#fff!important}body::before,body::after,*::before,*::after{backdrop-filter:none!important;filter:none!important}a{color:#0b57d0!important;text-decoration:none!important}[style*=\"position:fixed\"],[style*=\"position: sticky\"]{position:static!important}header nav footer aside[role=\"dialog\"]{display:none!important}</style></head>')\n    if len(html.encode()) > PDF_MAX_SOURCE_BYTES:\n        raise ValueError('print source too large')\n    printable_path.write_text(html)\n    return printable_path\n\n\ndef print_pdf(input_html: Path, output_pdf: Path):\n    cmd = [\n        CHROMIUM_BIN,\n        '--headless=new',\n        '--disable-gpu',\n        '--no-sandbox',\n        '--print-to-pdf=' + str(output_pdf),\n        '--allow-file-access-from-files',\n        '--disable-features=HttpsUpgrades,BlockInsecurePrivateNetworkRequests',\n        input_html.as_uri(),\n    ]\n    subprocess.run(cmd, capture_output=True, text=True, timeout=PDF_PAGE_TIMEOUT, check=True)\n\n\ndef build_pdf_pack(entries, build_stamp: str):\n    selected = entries[:PDF_MAX_ARTICLES]\n    if not selected:\n        return None\n    pdf_dir = OUTPUT_DIR / PDF_DIRNAME\n    pdf_dir.mkdir(parents=True, exist_ok=True)\n    article_pdfs = []\n    built = []\n    for idx, entry in enumerate(selected, start=1):\n        try:\n            printable = make_pdf_printable_copy(entry)\n            pdf_name = f'article-{idx:03d}-{slug(entry[\"link\"])}.pdf'\n            pdf_path = pdf_dir / pdf_name\n            print_pdf(printable, pdf_path)\n            article_pdfs.append(str(pdf_path))\n            built.append({\n                'title': entry['title'],\n                'path': f'/rss-offline/{PDF_DIRNAME}/{pdf_name}',\n            })\n            print('PDF_OK', entry['link'])\n        except Exception as e:\n            print('PDF_ERR', entry['link'], e)\n    if not article_pdfs:\n        return None\n    merged_filename = f'{PDF_BASENAME}-{build_stamp}.pdf'\n    merged_path = OUTPUT_DIR / merged_filename\n    subprocess.run(['pdfunite', *article_pdfs, str(merged_path)], check=True)\n    return {\n        'path': f'/rss-offline/{merged_filename}',\n        'count': len(built),\n        'articles': built,\n    }\n\n\ndef list_pdf_archive():\n    pdfs = []\n    pattern = re.compile(rf'^{re.escape(PDF_BASENAME)}-\\d{{4}}-\\d{{2}}-\\d{{2}}(?:_\\d{{2}}-\\d{{2}})?\\.pdf$')\n    for path in sorted(OUTPUT_DIR.glob(f'{PDF_BASENAME}-*.pdf'), reverse=True):\n        if not pattern.match(path.name):\n            continue\n        stat = path.stat()\n        pdfs.append({\n            'name': path.name,\n            'path': f'/rss-offline/{path.name}',\n            'size_mb': round(stat.st_size / (1024 * 1024), 1),\n            'date_label': datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).astimezone().strftime('%Y-%m-%d %H:%M'),\n        })\n    return pdfs\n\n\ndef build():\n    DATA_DIR.mkdir(parents=True, exist_ok=True)\n    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)\n    build_stamp = datetime.now().astimezone().strftime('%Y-%m-%d')\n    sources = load_sources()\n    cutoff = int((datetime.now(timezone.utc) - timedelta(days=DAYS)).timestamp())\n    entries = []\n\n    for source in sources:\n        try:\n            response = fetch(source['feedUrl'])\n            xml_text = response.text\n            root = ET.fromstring(xml_text)\n        except Exception as e:\n            print('FEED_ERR', source['name'], e)\n            continue\n\n        items = []\n        if root.tag.endswith('rss'):\n            channel = root.find('channel')\n            items = channel.findall('item') if channel is not None else []\n        else:\n            items = root.findall('{http://www.w3.org/2005/Atom}entry')\n\n        for item in items:\n            title = text(item, ['title', '{http://www.w3.org/2005/Atom}title']) or source['name']\n            link = entry_links(item)\n            if not link:\n                continue\n            link = urljoin(source['siteUrl'], link)\n            ts = parse_timestamp(text(item, ['pubDate', '{http://www.w3.org/2005/Atom}updated', '{http://www.w3.org/2005/Atom}published']))\n            if ts and ts < cutoff:\n                continue\n            entries.append({\n                'feed': source['name'],\n                'feed_url': source['feedUrl'],\n                'title': title,\n                'link': link,\n                'timestamp': ts,\n                'capture_mode': source['captureMode'],\n            })\n\n    entries.sort(key=lambda item: item['timestamp'], reverse=True)\n    entries = entries[:MAX_ENTRIES]\n\n    deduped = []\n    seen_links = set()\n    for entry in entries:\n        key = entry['link']\n        if key in seen_links:\n            continue\n        seen_links.add(key)\n        deduped.append(entry)\n    entries = deduped\n\n    archived = []\n    rendered_used = 0\n    for entry in entries:\n        try:\n            wants_rendered = (entry.get('capture_mode') or DEFAULT_CAPTURE_MODE) == 'rendered'\n            page_html, used_mode = capture_page(entry, rendered_allowed=(rendered_used < MAX_RENDERED_ENTRIES or not wants_rendered))\n            key = slug(entry['link'])\n            article_file = OUTPUT_DIR / f'article-{key}.html'\n            entry['local_path'] = f'/rss-offline/article-{key}.html'\n            entry['date_label'] = datetime.fromtimestamp(entry['timestamp'] or time.time(), tz=timezone.utc).astimezone().strftime('%Y-%m-%d %H:%M')\n            entry['captured_mode'] = used_mode\n            article_file.write_text(make_local_copy(entry, page_html, key))\n            if used_mode == 'rendered':\n                rendered_used += 1\n            archived.append(entry)\n            print('OK', used_mode, entry['link'])\n        except Exception as e:\n            print('ARTICLE_ERR', entry['link'], e)\n\n\n[27 more lines in file. Use offset=481 to continue.]"}],"isError":false,"timestamp":1780214939777}}
{"type":"message","id":"b56042fa","parentId":"b237e020","timestamp":"2026-05-31T08:09:01.883Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_qeJfikP4zdWmpIDAn55fV6RK|fc_0f029265420fe376016a1bec9d52888191bbc65df39c100023","name":"read","arguments":{"path":"/home/sebas/work/projects/rss-offline/build.py","offset":481,"limit":120}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":2546,"output":35,"cacheRead":22016,"cacheWrite":0,"totalTokens":24597,"cost":{"input":0.006365000000000001,"output":0.000525,"cacheRead":0.005503999999999999,"cacheWrite":0,"total":0.012394}},"stopReason":"toolUse","timestamp":1780214939778,"responseId":"resp_0f029265420fe376016a1bec9bd88c8191b7f971d6c98d1aca"}}
{"type":"message","id":"70c9ae63","parentId":"b56042fa","timestamp":"2026-05-31T08:09:01.884Z","message":{"role":"toolResult","toolCallId":"call_qeJfikP4zdWmpIDAn55fV6RK|fc_0f029265420fe376016a1bec9d52888191bbc65df39c100023","toolName":"read","content":[{"type":"text","text":"    pdf_info = build_pdf_pack(archived, build_stamp)\n\n    index = OUTPUT_DIR / 'index.html'\n    sw = OUTPUT_DIR / 'sw.js'\n    offline = OUTPUT_DIR / 'offline.html'\n    json_index = OUTPUT_DIR / 'entries.json'\n\n    json_index.write_text(json.dumps(archived, ensure_ascii=False, indent=2))\n\n    pdf_archive = list_pdf_archive()\n    pdf_cards = []\n    for item in pdf_archive:\n        pdf_cards.append(f'''<article class=\"item\"><h2><a href=\"{escape(item['path'])}\" download>{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n\n    local_paths = [entry['local_path'] for entry in archived]\n    index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n\n    offline.write_text('<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>rss-offline sin conexión</title><style>body{font-family:system-ui,sans-serif;background:#07111f;color:#fff;display:grid;place-items:center;min-height:100vh;padding:24px;margin:0}main{max-width:520px;padding:24px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.05)}a{color:#8dc3ff}</style></head><body><main><h1>Sin conexión</h1><p>La lista y copias locales visitadas deberían seguir disponibles.</p><p><a href=\"/rss-offline/\">Volver a rss-offline</a></p></main></body></html>')\n\n    precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + ([pdf_info['path']] if pdf_info else []) + [entry['local_path'] for entry in archived]\n    sw_version = f\"rss-offline-v{int(time.time())}\"\n    sw.write_text(f\"\"\"const V='{sw_version}';const CORE=V+'-core';const PAGES=V+'-pages';const PRECACHE={json.dumps(precache_paths, ensure_ascii=False)};self.addEventListener('install',e=>{{e.waitUntil(caches.open(CORE).then(c=>c.addAll(PRECACHE)));self.skipWaiting();}});self.addEventListener('activate',e=>{{e.waitUntil(caches.keys().then(keys=>Promise.all(keys.filter(k=>k!==CORE&&k!==PAGES).map(k=>caches.delete(k)))).then(()=>self.clients.claim()));}});self.addEventListener('fetch',e=>{{const r=e.request;const u=new URL(r.url);if(r.method!=='GET'||u.origin!==self.location.origin||!u.pathname.startsWith('/rss-offline/'))return;e.respondWith((async()=>{{const core=await caches.open(CORE);const pages=await caches.open(PAGES);const cached=await core.match(r)||await pages.match(r);try{{const res=await fetch(r);if(res.ok) await pages.put(r,res.clone());return res;}}catch{{return cached||await core.match('/rss-offline/offline.html')||Response.error();}}}})());}});\"\"\")\n\n\nif __name__ == '__main__':\n    build()\n"}],"isError":false,"timestamp":1780214941884}}
{"type":"message","id":"807fba56","parentId":"70c9ae63","timestamp":"2026-05-31T08:09:09.619Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bec9ebae48191908e345ea8663d61\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG-ylwWYZfBivp5jejywwxPDlb_bq11V0ZQFbYwTjrywKRAF7J0vYHdatGEr1Alsn0VaJRxoFXymx4JwtvvBrHkAniLQi5FCnG9J4jFy5vRBHziyeq851hDBfwwGP1vTaE3WTVW2eauXgAbN2dnlHDCvXv4ZnpkztX8fd6DX2-zyWcbP6Zph94Nfql7c3FWPtRi4-DPHTBc3GAydZx-yC9Z5xtEabanANhBAcz5oGQ7Ay5FO_mxUUktf1G5SEzfQHYra8r9FzGq5ytoNSx_-x25aE3CfbpRn0MAxVdjLIEallOrF8dcI09DeoyX2ZMBZDe2gMY896Q7S6RBPx0FnaAVn7bcTBz1TWHksI4IHfPKdSFV_uUdKA78GyOzkpAM0AuJomG8rsvVTlnoxNh-_lVqqoZT_LW8maP9VsmwsM-v7OUSJc4wSurO6NtB4f7z5Mo2BIrYbyQX0r8y6fcEPpdBpEaMJYpGcp51Ev9ZdBswNmx-0jNqC17O3pPK3sZ9A5RpOJNMotfmPK1MjcoxUS0FWgukjdZ3mJIWWn3Mmm3FbsPtpyBFPJxRXIb2uwLtgwxetX9PHcg9JNJTqWLgtVg9WoDOTDpwU66nJPHfMzywdzxpDJFN84zqDZba6NMJzgOA8DyKsSAjLtJ5Xbqc9D5sZ0l4MIMVwvnSDWwl1IF8fEIAIM5VYMNi5mlLJWx9nNqZ3LjXshOujMHg1X2anMAnVRG_qQitT5z537VInHl87X_VjpI-9FaM0nEthlev_ERMzT5HcMVxDaeyA1b_9Typ0xKP3TNsLbFnOJm6Y0_Cob7w_LDfdeGfer0IBhLu68GDJjPiPj2Ildk85N5qEmG3wS0qaQINbOEubl3wrfhLBH1xg1ZXzlodu93NyEwZw-LUfK5lqLy8ogry21i7wM8aHAoOKplAjNiy7JWLKo6iKrqKldAZj9-8ReDIGNq42AKbBpUTLBCs51rgTiXImI-Ln7M00p0SNuD-yYSDCMDRyZ51b-e0AXo8aIMCT8NVAwHpguSqW3zYSiv2BmD5RwUIw7fKWmDhHr-Ug31-K_Qp1gLHRlZAn9mcmZE_PH_TSizmhnPaoz0i1kn8_8jdcEGIkpbqn1qFsbIvEG5SatMIoMZdXKWazRVJkGb-4gTYMW-WgCKwQzSksFxmEiBiA11Ts6qtv_YfMqI9eSr78-WE6zEJDl7ihdxCPSIuaelYngelRYt9bpnK1n4vb7FmXEWUaTJpPe8z_tyiWQYZ3OzRHLwh06g31ZDFEQCy89CRHRegS06k5B6Hqn3aZjrVGh9HRVFWV5-c5gfcYzoj-NaCgnrPNI2hHAHtKWurNgF4Ec3Z_VIPFAXc6p_daSgd2HFwaRUxvM7yMmjHqLNksrbSZ4-_cy6EzyBQupHyr0yK1p9zivNDS5qRewJnAypuJxyjAhTavyuqpr6dZIe49CPhCt05cjGLGgwKzWg0OoUZRKOEiF2ltZWWFu9hQRCqqBglwCbbOlcBSC86YFOpMtxzXGTcvvUJn096-7InvA1SWhzTKmrHzzeu4yKqXWEf2lHK3V6QkIi_8kpcnduk19Vo-VQXm-5jPy3QJtMmIXU3rE3dPjI4es3-p___IDvijNDq4uM-gxq_SBU_P7cynfF3A9ErE=\",\"summary\":[]}"},{"type":"toolCall","id":"call_U8o8cCrxaqsJ6UDCZsKXHXEx|fc_0f029265420fe376016a1beca58f7881919bd6879d6effc054","name":"bash","arguments":{"command":"python - <<'PY'\nimport requests\nu='https://ballbox-first.emperor-ratio.ts.net/rss-offline/'\ntext=requests.get(u,timeout=20).text\nprint(text[:1200])\nPY","timeout":30}},{"type":"toolCall","id":"call_gYE1z0mdXHrlxEwDf1rJA8Ud|fc_0f029265420fe376016a1beca58f88819195f8565a0a48dcc6","name":"bash","arguments":{"command":"python - <<'PY'\nimport requests,re\nidx=requests.get('https://ballbox-first.emperor-ratio.ts.net/rss-offline/',timeout=20).text\nm=re.search(r'href=\"([^\"]+\\.pdf)\"',idx)\nprint('pdf',m.group(1) if m else 'none')\nif m:\n    r=requests.get('https://ballbox-first.emperor-ratio.ts.net'+m.group(1),timeout=20,allow_redirects=True)\n    print('status',r.status_code)\n    for k,v in r.headers.items():\n        if k.lower() in ('content-type','content-disposition','x-content-type-options','content-security-policy'):\n            print(k+':',v)\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":6360,"output":377,"cacheRead":19456,"cacheWrite":0,"totalTokens":26193,"cost":{"input":0.0159,"output":0.005655,"cacheRead":0.004863999999999999,"cacheWrite":0,"total":0.026419}},"stopReason":"toolUse","timestamp":1780214941885,"responseId":"resp_0f029265420fe376016a1bec9df6b48191ad171cdc37de192f"}}
{"type":"message","id":"dc7829fb","parentId":"807fba56","timestamp":"2026-05-31T08:09:09.920Z","message":{"role":"toolResult","toolCallId":"call_U8o8cCrxaqsJ6UDCZsKXHXEx|fc_0f029265420fe376016a1beca58f7881919bd6879d6effc054","toolName":"bash","content":[{"type":"text","text":"<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{box-sizing:border-box}body{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}main{max-width:960px;margin:0 auto;padding:24px}h1,h2{margin:0 0 8px}.muted{color:rgba(255,255,255,.68)}.item{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}.item h2{margin:0 0 6px;font-size:1.1rem}a{color:#8dc3ff;text-decoration:none}a:hover{text-decoration:underline}.meta{font-size:.92rem;color:rgba(255,255,255,.72)} .top{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}section{margin-top:28px}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artÃ­culos nuevos.</p></div><p class=\"muted\">37 artÃ­culos Â· Ãºltimos 14 dÃ­as</p></div><section><h2>Archivo de PDFs</h2><article class=\"item\"><h2><a href=\"/rss-offline/rss-offline-2026-05-30.pdf\" download>rss-offline-2026-05-30.pdf</a></h2><p class=\"meta\">2026-05-30 06:02 Â· 11.2 MB</p></article><article class=\"item\"><h2><a h\n"}],"isError":false,"timestamp":1780214949920}}
{"type":"message","id":"4acf7dc5","parentId":"dc7829fb","timestamp":"2026-05-31T08:09:09.920Z","message":{"role":"toolResult","toolCallId":"call_gYE1z0mdXHrlxEwDf1rJA8Ud|fc_0f029265420fe376016a1beca58f88819195f8565a0a48dcc6","toolName":"bash","content":[{"type":"text","text":"pdf /rss-offline/rss-offline-2026-05-30.pdf\nstatus 200\nContent-Type: application/pdf\n"}],"isError":false,"timestamp":1780214949920}}
{"type":"message","id":"c5aed970","parentId":"4acf7dc5","timestamp":"2026-05-31T08:09:33.328Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Revising implementation**\n\nI need to implement a change. I'm thinking about removing the download feature and adding a more robust visited style instead. Maybe I should consider whether I need a target? Actually, I think I don’t. It seems like I can just focus on editing and rebuilding. Let's get started on that!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1beca6efbc8191820097d0a6a7295c\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG-yotyjgCeCV-WhJVU627X-WuDDPhGiY5OSLucQdYIUmG2vszKMnL0KRu1fVos0ZIVBre728SDMge-1KM1jRU5M97UkZaFHGAFsj0NrusT0t5u28oy0VXVw55SRXfqMEEKZZmWEb2iAlkxdItavktnzLcHQE5LkoZaq7SXov3LIGvmvqqiNd4hqz82-LwSAYfoOasFwGxDTL2FddRAUAOnay9h6CN7rYdGZ7CrqXLkz04g7k8X_u0VxIHLcmFG5vq-JgB08URmhSb262sx-k8LFtGmOe_nLjrz2civ_WYJ9MbVzvantMf25viHN2Gt034NbUy2gXAwycsQdUTjUMmAIzEw49Y9VDRTNzKT28hs7YJM8zRMPB-KK-KJIwJ0Y41s3A313vlK9aMLOscZ93pd8Bel8vjtmfQU6lkeIMf7tv0fJLaSCYXqr1mADm1xZXJ5_dSi0x8cJxHyn2kx24efi2qWRo8_Ho_evZvH_7q3lxg2UPuFTTG1zIJEBs5SS_KzFeVZ6w2YCavK1u6-RYU1h6zWULCN6mZ8x3Pl2cU13Y7X41ngH69roJfkT5vkGy9Nuq6t0iYLV4nt4gocEIMKemCTqjXf_gj3mGzyVsB82HqwOhTYedRWhGtRyBgcMLvo1GXibO546GPvUApg1Tp071VLj-N7LNx0H9LnmKAdLpSZ0z_E7AMkFN7Q_yi5GHDyfNkmcRdxoDBZdtsQPOHh1j9rRGZuEUOUnvqALyn6qRAm1P61wYe5yAGjzLFyNxpM1RLCH211MGEIf08A-N937nJe6Oh68I9KumPeYkNcjDN5mBl0zcZEXVeQSOMhbxzTZR74zTtTH2q266OX6hjYLVU4L3EzTF2d3z7PCFZ9cbaSTr5olsRyRqdZBQzxifFdpqP0jafXdEuLpDaoHMfy7T-WvMgUGPcXulls42RWCVgti_k8brhsF4Hz0Pwl-CX6rSipP9qpRJ-22vtqqsUCdfZtfrl8N7ZIsAiSZ7PUq4MHbnyLz68Xah0FKYK4zsDjGU0S8pzmkzLsYC2T1gpbioFYUo4parXLY9jcGmubYjsT-DZ9v6pDm8Lg3Q3els5VMr\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Revising implementation**\\n\\nI need to implement a change. I'm thinking about removing the download feature and adding a more robust visited style instead. Maybe I should consider whether I need a target? Actually, I think I don’t. It seems like I can just focus on editing and rebuilding. Let's get started on that!\"}]}"},{"type":"toolCall","id":"call_de5VRpTr8WfmUVAP0NQbVK31|fc_0f029265420fe376016a1beca8d72c8191b8774c32f8fe8602","name":"edit","arguments":{"path":"/home/sebas/work/projects/rss-offline/build.py","edits":[{"oldText":"    for item in pdf_archive:\n        pdf_cards.append(f'''<article class=\"item\"><h2><a href=\"{escape(item['path'])}\" download>{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n","newText":"    for item in pdf_archive:\n        pdf_cards.append(f'''<article class=\"item\"><h2><a class=\"pdf-link\" href=\"{escape(item['path'])}\">{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n"},{"oldText":"    index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n","newText":"    index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:visited{{color:#c69cff}}a:hover{{text-decoration:underline}}.pdf-link:visited{{color:#d9b8ff}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n"}]}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1059,"output":1238,"cacheRead":25600,"cacheWrite":0,"totalTokens":27897,"cost":{"input":0.0026475,"output":0.01857,"cacheRead":0.0063999999999999994,"cacheWrite":0,"total":0.0276175}},"stopReason":"toolUse","timestamp":1780214949921,"responseId":"resp_0f029265420fe376016a1beca5fb38819197224866ba0a3ebd"}}
{"type":"message","id":"90797d4f","parentId":"c5aed970","timestamp":"2026-05-31T08:09:33.406Z","message":{"role":"toolResult","toolCallId":"call_de5VRpTr8WfmUVAP0NQbVK31|fc_0f029265420fe376016a1beca8d72c8191b8774c32f8fe8602","toolName":"edit","content":[{"type":"text","text":"Successfully replaced 2 block(s) in /home/sebas/work/projects/rss-offline/build.py."}],"details":{"diff":"     ...\n 489 \n 490     pdf_archive = list_pdf_archive()\n 491     pdf_cards = []\n 492     for item in pdf_archive:\n-493         pdf_cards.append(f'''<article class=\"item\"><h2><a href=\"{escape(item['path'])}\" download>{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n+493         pdf_cards.append(f'''<article class=\"item\"><h2><a class=\"pdf-link\" href=\"{escape(item['path'])}\">{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n 494 \n 495     local_paths = [entry['local_path'] for entry in archived]\n-496     index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n+496     index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:visited{{color:#c69cff}}a:hover{{text-decoration:underline}}.pdf-link:visited{{color:#d9b8ff}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n 497 \n 498     offline.write_text('<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>rss-offline sin conexión</title><style>body{font-family:system-ui,sans-serif;background:#07111f;color:#fff;display:grid;place-items:center;min-height:100vh;padding:24px;margin:0}main{max-width:520px;padding:24px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.05)}a{color:#8dc3ff}</style></head><body><main><h1>Sin conexión</h1><p>La lista y copias locales visitadas deberían seguir disponibles.</p><p><a href=\"/rss-offline/\">Volver a rss-offline</a></p></main></body></html>')\n 499 \n 500     precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + ([pdf_info['path']] if pdf_info else []) + [entry['local_path'] for entry in archived]\n     ...","patch":"--- /home/sebas/work/projects/rss-offline/build.py\n+++ /home/sebas/work/projects/rss-offline/build.py\n@@ -489,12 +489,12 @@\n \n     pdf_archive = list_pdf_archive()\n     pdf_cards = []\n     for item in pdf_archive:\n-        pdf_cards.append(f'''<article class=\"item\"><h2><a href=\"{escape(item['path'])}\" download>{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n+        pdf_cards.append(f'''<article class=\"item\"><h2><a class=\"pdf-link\" href=\"{escape(item['path'])}\">{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n \n     local_paths = [entry['local_path'] for entry in archived]\n-    index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:hover{{text-decoration:underline}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n+    index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:visited{{color:#c69cff}}a:hover{{text-decoration:underline}}.pdf-link:visited{{color:#d9b8ff}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n \n     offline.write_text('<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>rss-offline sin conexión</title><style>body{font-family:system-ui,sans-serif;background:#07111f;color:#fff;display:grid;place-items:center;min-height:100vh;padding:24px;margin:0}main{max-width:520px;padding:24px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.05)}a{color:#8dc3ff}</style></head><body><main><h1>Sin conexión</h1><p>La lista y copias locales visitadas deberían seguir disponibles.</p><p><a href=\"/rss-offline/\">Volver a rss-offline</a></p></main></body></html>')\n \n     precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + ([pdf_info['path']] if pdf_info else []) + [entry['local_path'] for entry in archived]\n","firstChangedLine":493},"isError":false,"timestamp":1780214973405}}
{"type":"message","id":"bbf60bb4","parentId":"90797d4f","timestamp":"2026-05-31T08:09:39.671Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_KbQG0t5aD1q68Uf7Nf12jSLE|fc_0f029265420fe376016a1becc07c5c81918a4a8352a7e8b07b","name":"bash","arguments":{"command":"cd /home/sebas/work/projects/rss-offline && ./build.py >/tmp/rss-offline-build.out && python - <<'PY'\nimport requests,re\nidx=requests.get('https://ballbox-first.emperor-ratio.ts.net/rss-offline/',timeout=20).text\nprint('has download attr', 'download' in idx)\nprint('has visited css', 'a:visited' in idx and '.pdf-link:visited' in idx)\nm=re.search(r'<a class=\"pdf-link\" href=\"([^\"]+\\.pdf)\"', idx)\nprint('pdf-link', bool(m), m.group(1) if m else '')\nPY","timeout":300}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":3863,"output":161,"cacheRead":24064,"cacheWrite":0,"totalTokens":28088,"cost":{"input":0.009657500000000001,"output":0.002415,"cacheRead":0.006016,"cacheWrite":0,"total":0.0180885}},"stopReason":"toolUse","timestamp":1780214973407,"responseId":"resp_0f029265420fe376016a1becbf637081919e29139abddf95ca"}}
{"type":"message","id":"adb96cfa","parentId":"bbf60bb4","timestamp":"2026-05-31T08:14:39.693Z","message":{"role":"toolResult","toolCallId":"call_KbQG0t5aD1q68Uf7Nf12jSLE|fc_0f029265420fe376016a1becc07c5c81918a4a8352a7e8b07b","toolName":"bash","content":[{"type":"text","text":"Command timed out after 300 seconds"}],"details":{},"isError":true,"timestamp":1780215279693}}
{"type":"message","id":"d93e29c5","parentId":"adb96cfa","timestamp":"2026-05-31T08:14:44.761Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Considering build inspection**\n\nI think I might need to build slowly and check the output, maybe using a temporary file? It seems like a shorter inspection could be helpful, especially if the build is still running. Perhaps I should try rerunning the process with a larger timeout to give it some extra time. That way, I can get a clearer picture of what’s happening. It's always good to be thorough in this kind of situation!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bedf102d48191b622be2f40cb0aa6\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG-3zcN2VfUqewElF1wQyFCtYD-JQl5GKgNKYFk64MZLsJFrLMxNJssvEGc-OH12qYSxVno3PegLzitg5JZDCr691dkMZkv4SBd2YS6VrBCAQESsdnxeC177MLXmqjpuUIdl4fcX6JMzyLpYNXVYVBbxE8eLVq6n1BXtBhc5fl5rJpQSe0d38DP7GZ_080RcwBFZuL3eRkVDjS0Km_MGqI4PfGNC4xEjZUoknZ-9QU4TiEfb6ch0oPVIasp_UZ5_AlAH1AqdLukb7qFtrWnUFqO7-h6HS8hQWoiPNfU0khn2VbfWhRn8GH1wdsmVtMD6LAKUW2sYv9giGpFA3nZVYB971QFBTV1fkhbNXSXSFjaQcEudDzawmTjG1pk0T0Dr39nO7d4Qzi5VwEtXCto7Fu0bWLNppImH7VI9LmYPGBENNXb99lGaeOCDDM2XqaoriUvVNohVaTWk5XjXLUTiJCzOpqXj-6ekMG91fS9fC82Cb5bXCCiYKqlYGzKwM11zvGyzgExdUjxqqaOXuHf8PxoZLWmRDzuI86RopNFgF3Y4OKd_nmyi6lkBHcX2twY94jijwoXHOgnbzKye9qhQLlHGOS906U3K_GFoDv1pVweSUK-JsfN5tzE66BhlEoftFJQfQOugLdkxsKUU7DwNb8zfpkPMvYYf6B2I7ZDgIWN53F9MOjd7H2d4MpHUdguRVRDQp9yg_3bh5KKeG-S7KXu1RwBKX4niOHd15WOcUvmrml4J_zJT9mC8VckaGqwPD5lCGX7xnpi6-lGzj21M-DjdCA94IQdx9eHFbRZVlzKTHFCqBRO2oVPEBXFx-XBSxogsq7g8hK3cE0G3KibkApJyYJX3QnshfDwiDh35FgBTqYiic2Mu4dBsalVH0bFa2_w9EN1Bf_juj6tNv3SYOuH-2etNUX03R2vht40HID3fBIIjk9KsZWD18VbhekqIbMuzstjseudQnHrmajHQcd9ndB-2qsPinls4K7X4jjgEFSi2Z0ZMb83oI0Bd-KwxSHkk5ERCH95OllGwGx7g0BfyJVbxj0MkElxfyKOgOlOQrBrXsA8EykviD4MGtjhthgltuODSqv1ybZT6i-RDNyj94cA==\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Considering build inspection**\\n\\nI think I might need to build slowly and check the output, maybe using a temporary file? It seems like a shorter inspection could be helpful, especially if the build is still running. Perhaps I should try rerunning the process with a larger timeout to give it some extra time. That way, I can get a clearer picture of what’s happening. It's always good to be thorough in this kind of situation!\"}]}"},{"type":"toolCall","id":"call_2meZoIXUIX0wkq1n94jUFi5R|fc_0f029265420fe376016a1bedf33c6c8191943c3d124ba57a23","name":"bash","arguments":{"command":"cd /home/sebas/work/projects/rss-offline && ./build.py >/tmp/rss-offline-build.out && tail -40 /tmp/rss-offline-build.out && python - <<'PY'\nimport requests,re\nidx=requests.get('https://ballbox-first.emperor-ratio.ts.net/rss-offline/',timeout=20).text\nprint('has download attr', 'download' in idx)\nprint('has visited css', 'a:visited' in idx and '.pdf-link:visited' in idx)\nm=re.search(r'<a class=\"pdf-link\" href=\"([^\"]+\\.pdf)\"', idx)\nprint('pdf-link', bool(m), m.group(1) if m else '')\nPY","timeout":900}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":28106,"output":204,"cacheRead":0,"cacheWrite":0,"totalTokens":28310,"cost":{"input":0.07026500000000001,"output":0.0030600000000000002,"cacheRead":0,"cacheWrite":0,"total":0.073325}},"stopReason":"toolUse","timestamp":1780215279695,"responseId":"resp_0f029265420fe376016a1bedf00568819190d9332e5e770dab"}}
{"type":"message","id":"0f64597d","parentId":"d93e29c5","timestamp":"2026-05-31T08:29:10.843Z","message":{"role":"toolResult","toolCallId":"call_2meZoIXUIX0wkq1n94jUFi5R|fc_0f029265420fe376016a1bedf33c6c8191943c3d124ba57a23","toolName":"bash","content":[{"type":"text","text":"Traceback (most recent call last):\n  File \"/home/sebas/work/projects/rss-offline/./build.py\", line 506, in <module>\n    build()\n    ~~~~~^^\n  File \"/home/sebas/work/projects/rss-offline/./build.py\", line 488, in build\n    json_index.write_text(json.dumps(archived, ensure_ascii=False, indent=2))\n    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.13/pathlib/_local.py\", line 557, in write_text\n    return PathBase.write_text(self, data, encoding, errors, newline)\n           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.13/pathlib/_abc.py\", line 651, in write_text\n    with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:\n         ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.13/pathlib/_local.py\", line 539, in open\n    return io.open(self, mode, buffering, encoding, errors, newline)\n           ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nPermissionError: [Errno 13] Permission denied: '/var/www/rss-offline/entries.json'\n\n\nCommand exited with code 1"}],"details":{},"isError":true,"timestamp":1780216150843}}
{"type":"message","id":"6d7f4f03","parentId":"0f64597d","timestamp":"2026-05-31T08:29:15.728Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bf15829248191ac21643feaea24e3\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_FZQOvFNCOyyceHur7I7oWDYWVRpYiypqmL5omPkdGr9yEdXZff_28L-D73kwA7PsPwjBCbgMTEZ1ajAVAMoo6QyDSB00BZML2FcNytLlLF3FSV9t2mq8haSW9Zxm149i2mWh9PLZkw0PCSFKdYQrIqmaO3BnGbiw_WrGob9ghhsZlwsggpSDdHKUUnVps0E-ETm3y-7k9_IkTHTyqcq8jPuUBdcBB5XLAP8MN6EMgMFk1zKYUSiGvw6BGzGiqmGosb1c-ydCzUHYkG8uWWMaoTH-9Hgx5g3nN8m4Yt_DgonTJNjV3QYXpnZrnYwToKUruFzgfwkglTMFTK-ND3BYAc-4BRfyDVkWy5j3WVXopsd0kjc6eT91kGHlNSfPDodandQECzbJBVoqh9JJqSe89WtyhXEokwQ7a-z-BjsBGiXi2VsYP3K1bP5Ocq0W0Ug0S7Uiy8v_Twq2zmRChesxbD9N-40h_ZCjcwDDcJqdeTgdVe6IDfvCXDa3iWoXdEv-jqhgaagyLU1eoCx1vOooP05w0-Zp2z8NnHIq4Cs_19T86THEB44o3IcY798lslmlVu7mcGLAlLtMJWt_X02Zd6RCFlHfSzi07mAmzCuNZgT9ww-9IFRloE21-bRQzB3FcWWDfYv6trMnAKyyejHt_WO2uYNrNMuMi6SY5WGbefbpWdxgRqCNQ0U4e7O-f49tTZXXazYs4JUmJNTmLy1uG4cast2hv6YCc94TTq8-7NzQk-gLOg_0gAxSvm0jT8Y_DXBHFPEUZxUQt9ivxf4-1DkH4NQ_cl_hp5CzU9IY0tfZjAjsxPtcVNE7l1GiVsPhqcfI4eCYn1Ik-Z831CttK4YroHPyhpT6S2_rM71Zye040Zk54Mc0K1HNZ5hmR2r-snKZbTI4pIkEmE1rnAw2bflfFMhddgYJ0p7TccyyEHKCoa1wpYupuR3ILFneKMDfZ9zT1DAuTzCLVXMIW7iIB1p8ZygL_Gjq08Zwsw2cyzplk=\",\"summary\":[]}"},{"type":"toolCall","id":"call_m4KRnL8zthgCOh41KOCchgES|fc_0f029265420fe376016a1bf15910e88191addead5b1c4f1800","name":"bash","arguments":{"command":"cd /home/sebas/work/projects/rss-offline && sudo ./build.py >/tmp/rss-offline-build.out && tail -20 /tmp/rss-offline-build.out && python - <<'PY'\nimport requests,re\nidx=requests.get('https://ballbox-first.emperor-ratio.ts.net/rss-offline/',timeout=20).text\nprint('has download attr', 'download' in idx)\nprint('has visited css', 'a:visited' in idx and '.pdf-link:visited' in idx)\nm=re.search(r'<a class=\"pdf-link\" href=\"([^\"]+\\.pdf)\"', idx)\nprint('pdf-link', bool(m), m.group(1) if m else '')\nPY","timeout":900}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":28603,"output":193,"cacheRead":0,"cacheWrite":0,"totalTokens":28796,"cost":{"input":0.0715075,"output":0.002895,"cacheRead":0,"cacheWrite":0,"total":0.0744025}},"stopReason":"toolUse","timestamp":1780216150844,"responseId":"resp_0f029265420fe376016a1bf15736ec8191ad0e86c58087ee04"}}
{"type":"message","id":"6420ce64","parentId":"6d7f4f03","timestamp":"2026-05-31T08:31:52.764Z","message":{"role":"toolResult","toolCallId":"call_m4KRnL8zthgCOh41KOCchgES|fc_0f029265420fe376016a1bf15910e88191addead5b1c4f1800","toolName":"bash","content":[{"type":"text","text":"OK simple https://github.blog/open-source/building-githubs-next-chapter-in-accessibility/\nOK simple https://leadershipintech.com/newsletters/2282-recognize-your-management-wins\nOK simple https://tailscale.com/changelog/#2026-05-21-client\nOK simple https://tailscale.com/changelog/#2026-05-21-service\nOK simple https://github.blog/security/investigating-unauthorized-access-to-githubs-internal-repositories/\nOK simple https://supernuclear.substack.com/p/case-study-tamera\nOK simple https://www.digitalocean.com/blog/inference-router-architecture\nOK simple https://tailscale.com/changelog/#2026-05-20-service\nOK simple https://simonw.substack.com/p/the-last-six-months-in-llms-in-five\nOK simple https://tailscale.com/changelog/#2026-05-19-service\nOK simple https://github.blog/news-insights/product-news/take-your-local-github-sessions-anywhere/\nOK simple https://leadershipintech.com/newsletters/2281-how-to-stay-resilient-in-a-difficult-job\nPDF_OK https://mariozechner.at/posts/2026-05-30-shitty-robot\nPDF_OK https://www.digitalocean.com/blog/ai-disruptors\nPDF_OK https://supernuclear.substack.com/p/the-half-assed-potluck-and-other\nPDF_OK https://gomakethings.com/articles/how-to-redirect-if-a-page-doesnt-exist-with-apache/\nPDF_OK https://simonw.substack.com/p/i-think-anthropic-and-openai-have\nPDF_OK https://tailscale.com/changelog/#2026-05-29-service\nPDF_OK https://martinalderson.com/posts/whats-going-on-with-gemini/?utm_source=rss&utm_medium=rss&utm_campaign=feed\nPDF_OK https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\nhas download attr False\nhas visited css True\npdf-link True /rss-offline/rss-offline-2026-05-31.pdf\n"}],"isError":false,"timestamp":1780216312764}}
{"type":"message","id":"8be0fba7","parentId":"6420ce64","timestamp":"2026-05-31T08:31:54.824Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bf1f9f4f081918a4cf405b2528b7a\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_H6mGFt6kDAZECpxpJFQ2ALZyQa2jAtw3Ic1mdj0tLAfMToHU9rZx8aynXamMi4uHnS1XGD7ZiAYOOINyCS4FyRMxfys9OZKZkxfZ-4P0tuRxeVEikMJhFNho1IMmBB9I2AvKDF8WRPJNGFWZBZS_o96VE1WxKhDeVw0IuGaR-9jQLqkQJJMoNKXzF17p3ACMZz66_hMQUMag7eo6y4En6EqM2BD2qaasmeE7ZIfahb8HI4LWKNKUVSSjjtS6r1LY1jeK23hSIG9h45F7YktzA4uHLPvwF5BgSB_W4hX86e1Xmw45tzZE42so7MDKcZhIGUWWfzKS-huX-9sqks9mbcMQ4RgSCI0-l9NLp54y7t_BI18yCG-DXYMOYYQOEWFlE5pqfXyrUcjqva9TTetRv_2Okh1iPwe7fe6NSJsf5GgTCrAMmqnIw53wt2Vp-zGTFG5fsUfnZI2cOzD32K-yxJyyQBTDbEL9TUL8DoKm-sAPhDCKUImXT-fP0Snmx2A6PCotaBMG6Z0dasKe9MQ-T10bYqli5bdMvF1aoAcIhzFUviY5Pe4NxRxOpcY_Yvt8iTvg-ZfQkSvFsDbk3X0TgLxIgpbltDB5uARyCw6vEmIH-k9FcgMZPRzltBYgX2oXXx4dLejLUiQlfco-U19hWFbEdisfmKpYnmYJj0KwPZVD00m7olnCj3l1wkNQMcdFrcUzP7UH9MAa8QdcEwWPKZKHOi3WkSiuXpyx6_XJQ6OMYEz7l_0TyNfS0IUa9P0WnfbBTSbsUCRdq_WSaXeItn3s3EWybbldkGotSzQBQuho78faaWrYPtSwj6vUlhQRy2-K9b6TwBped6QXKn0SEQk3yvROXi-uduzLkJqXGhLeBjTwekkN46ViDTSLB5qQW6DYY9BVu60rl1TQ3I9qduemZuiUZqMvolVaZRcKIHBBZtXQauPll4JXW7RglCB-EE\",\"summary\":[]}"},{"type":"toolCall","id":"call_2qkmKux4RYES5W0pcj5LaVpd|fc_0f029265420fe376016a1bf1faab2c8191afb93614e829a8fd","name":"todo","arguments":{"action":"update","id":1,"status":"completed"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1097,"output":36,"cacheRead":28160,"cacheWrite":0,"totalTokens":29293,"cost":{"input":0.0027425,"output":0.00054,"cacheRead":0.007039999999999999,"cacheWrite":0,"total":0.0103225}},"stopReason":"toolUse","timestamp":1780216312765,"responseId":"resp_0f029265420fe376016a1bf1f8d44c8191a5a4cb001fadc3ed"}}
{"type":"message","id":"3b44369a","parentId":"8be0fba7","timestamp":"2026-05-31T08:31:54.825Z","message":{"role":"toolResult","toolCallId":"call_2qkmKux4RYES5W0pcj5LaVpd|fc_0f029265420fe376016a1bf1faab2c8191afb93614e829a8fd","toolName":"todo","content":[{"type":"text","text":"Updated #1 (pending → completed)"}],"details":{"action":"update","params":{"action":"update","id":1,"status":"completed"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"}],"nextId":2},"isError":false,"timestamp":1780216314825}}
{"type":"message","id":"28e51a23","parentId":"3b44369a","timestamp":"2026-05-31T08:31:57.761Z","message":{"role":"assistant","content":[{"type":"text","text":"Sí. Ya quedó.\n\nCambio:\n- saqué `download` de los links a PDF\n- agregué estilo `a:visited` visible\n- rebuild del sitio hecho\n\nResultado:\n- al abrir un PDF desde\n  https://ballbox-first.emperor-ratio.ts.net/rss-offline/\n  el link ahora puede quedar marcado como leído con el `:visited` nativo del browser\n\nProbá refrescando la página después de abrir uno.  \nSi querés, también le meto un color más obvio para “leído”.","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1bf1fb9f90819180afc795708abab1\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":127,"output":115,"cacheRead":29184,"cacheWrite":0,"totalTokens":29426,"cost":{"input":0.0003175,"output":0.001725,"cacheRead":0.0072959999999999995,"cacheWrite":0,"total":0.0093385}},"stopReason":"stop","timestamp":1780216314826,"responseId":"resp_0f029265420fe376016a1bf1fae87c8191b1a258b3d2f347fd"}}
{"type":"message","id":"4af302e4","parentId":"28e51a23","timestamp":"2026-05-31T08:33:44.073Z","message":{"role":"user","content":[{"type":"text","text":"[telegram] dos nth mas:\nun boton al lado para descargar directo el pdf\nmantener el scroll position de los pdfs abiertos en la pagina, se puede?"}],"timestamp":1780216424071}}
{"type":"message","id":"661c787c","parentId":"4af302e4","timestamp":"2026-05-31T08:33:49.692Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Considering PDF scroll functionality**\n\nI need to figure out how to maintain the scroll position when dealing with PDFs. It seems I might be able to add a button and use localStorage to save the scroll position when returning from a PDF. Generally, opening the same tab keeps the scroll, but PDFs could replace the document. I wonder if it’s better to open the reader link in a new tab instead. I should consider implementing scroll storage on click and restoring it on load. Time to edit the build.py.","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bf2691b508191b526a377223e6ca7\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_Jt6-b6JKIpmGl_cksVzQ8tB0Sg43c36HzfCNbq8zmYcQDXGBwnoveVn8HewddtpdId5JfC8pCdNNd8LZrwZi13ZAQ39vagcFDkHRYNaEQ2HnRqVl0B7yI-aQImwO8ilioeJmdTdr59Ot9PI-Oi4ZMebvKYRh--Nf8znCnXn9KxdwpnRDRa7IZkS6fzwqBrUMhygAZKAn7rwnJ0iC2YugPCRUcPvStka8YQSTvriuCOnXi5-9XRDnqwq0cooUGvGhs1WC2EyEq1ecSg3WurOC-BaeVKwr0od4Gn7yIn98ANS7ZLTk8K7OcZYGU8j0itrkkx4UyFtIxrw9LB5J_aLAwWjzUBjcRGRlMRKnsdBkP56w84Ma0yxJyZjW6Mdd-l5Jxa65v0vm92qkcUtkjq98e1ftYNVyuqUHQAhlZNYIJ3izAS5re0pfYJfl8vRlEde5Cz3Fmjq1GL3YRCIbEbiLUIJD1sG42o8SelJM8F8nphy0Mg-dWhSJIQcM1fhKX2MH8gJvFC838nt_3K8PWOJZ9AJJ3ICsVaRM6AC5S-MVigcx_ZeBU2fLUk-lswNWnVvuQkg2tkaC-RScU_N6bdFTJCUcuyVwzs1XTI5D9MndZX6lBU-sFrNdSBYiP7fBhbcI1PDpkEJqiIeSEHG5pVW3EjEF8_NjNn_wY5_TclR8zUjTrhvWPOKwqZ41ecF5UOtU_0rysFioXepxsh3sT4VwaSCaIKVmmuqkTwEHSaB_7dujAVcs4T7Bn2pXOOYFY4O7lv5xnquSPo8pVIuoEDeINhkiwi3K_6oSqTfIXK4Dm4QCoJQxVX1WVZOTM2DILh6FyVmpCO-Dxi5ktE-FE1zgCP65pAptly3SWHgzyL8oQvgRJdP881KP5EA3V_VeBYVNWb0ihkrOka3Pylo7XxCrlmW4UGWPpketDXOcwPd1g3GRwf59jJ2cNvy_DV93G5GH_auzl6R0OjAFaK86pTLln6ecxzmMk3JvWctTS1RBBvlzQoKgK1nYLe614SVQguEddEykmUDVg3L4_62MZ_u6TmgVCSM62P_w9Sd7XWw_j7QZrJAy8BVbw_23gHpaVWBUtpNq9AyyeTjP9PQi4nJ6FyEj7oxMf4YyX_et96nqq8C5f9xNab9J_L3mIRizvYP8nl5i7dTfG-JTPglv9KuopKuzrscpcLRxJz8AXoCFkzDxAK45CWsCclGccI5wPVwWOOXiKXwpmm6LDisalahMdbR8v8m2Q88tHO34ulXfQcd6FX3B90UMmZvJVyAqkn9uSUYO6eryY-cdq8WZWfG-6AIY5ksmciokWbv_beyi686DXh_Rj5HnuXafWkrOak25AoteEo-Nzr9BqXch1M88G-S6WVj_2i1UoieJVWk42LxRyE2HLw6BkQmHy5p4x2XOZi5pFncX1DwziNa2WZwXqaDKzKnUY_f0AQVXZO4RaVeQ5hIMMhYNgNj0l1h29oiicJr8SPwJUS5MUCfhDZf-XJdKx7u487n_c4NBokxwPBC-mmY-8MjvrnqydDbcrzzn5Rj4DqmfoOQJ3bswsGClvSdA==\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Considering PDF scroll functionality**\\n\\nI need to figure out how to maintain the scroll position when dealing with PDFs. It seems I might be able to add a button and use localStorage to save the scroll position when returning from a PDF. Generally, opening the same tab keeps the scroll, but PDFs could replace the document. I wonder if it’s better to open the reader link in a new tab instead. I should consider implementing scroll storage on click and restoring it on load. Time to edit the build.py.\"}]}"},{"type":"toolCall","id":"call_WWtT9yFb8JufyAcrMViqxhpf|fc_0f029265420fe376016a1bf26d73b481919e7bb2fb5132e9a7","name":"todo","arguments":{"action":"create","subject":"Add direct PDF download button and preserve rss-offline scroll position","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","status":"in_progress","owner":"agent"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":24054,"output":183,"cacheRead":5120,"cacheWrite":0,"totalTokens":29357,"cost":{"input":0.06013500000000001,"output":0.002745,"cacheRead":0.0012799999999999999,"cacheWrite":0,"total":0.06416000000000001}},"stopReason":"toolUse","timestamp":1780216424074,"responseId":"resp_0f029265420fe376016a1bf2682a8c8191bc48b2e182433b77"}}
{"type":"message","id":"4c10694b","parentId":"661c787c","timestamp":"2026-05-31T08:33:49.693Z","message":{"role":"toolResult","toolCallId":"call_WWtT9yFb8JufyAcrMViqxhpf|fc_0f029265420fe376016a1bf26d73b481919e7bb2fb5132e9a7","toolName":"todo","content":[{"type":"text","text":"Created #2: Add direct PDF download button and preserve rss-offline scroll position (pending)"}],"details":{"action":"create","params":{"action":"create","subject":"Add direct PDF download button and preserve rss-offline scroll position","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","status":"in_progress","owner":"agent"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"pending","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"}],"nextId":3},"isError":false,"timestamp":1780216429693}}
{"type":"message","id":"4a3e524c","parentId":"4c10694b","timestamp":"2026-05-31T08:34:18.329Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_nTkZz5C6zA54BpnnHd2oMkXI|fc_0f029265420fe376016a1bf26fb1248191bf3ad161f7da3be7","name":"edit","arguments":{"path":"/home/sebas/work/projects/rss-offline/build.py","edits":[{"oldText":"    for item in pdf_archive:\n        pdf_cards.append(f'''<article class=\"item\"><h2><a class=\"pdf-link\" href=\"{escape(item['path'])}\">{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n","newText":"    for item in pdf_archive:\n        pdf_cards.append(f'''<article class=\"item\"><h2><a class=\"pdf-link\" href=\"{escape(item['path'])}\">{escape(item['name'])}</a> <a class=\"pdf-download\" href=\"{escape(item['path'])}\" download>descargar</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n"},{"oldText":"    index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:visited{{color:#c69cff}}a:hover{{text-decoration:underline}}.pdf-link:visited{{color:#d9b8ff}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n","newText":"    index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem;display:flex;gap:10px;align-items:center;flex-wrap:wrap}}a{{color:#8dc3ff;text-decoration:none}}a:visited{{color:#c69cff}}a:hover{{text-decoration:underline}}.pdf-link:visited{{color:#d9b8ff}}.pdf-download{{font-size:.78rem;padding:4px 8px;border:1px solid rgba(141,195,255,.35);border-radius:999px}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};const RSS_OFFLINE_SCROLL_KEY='rss-offline:index-scroll';const restoreScroll=()=>{{try{{const y=Number(sessionStorage.getItem(RSS_OFFLINE_SCROLL_KEY)||'0');if(y>0)requestAnimationFrame(()=>window.scrollTo(0,y));}}catch(_e){{}}}};const saveScroll=()=>{{try{{sessionStorage.setItem(RSS_OFFLINE_SCROLL_KEY,String(window.scrollY||window.pageYOffset||0));}}catch(_e){{}}}};if('scrollRestoration' in history)history.scrollRestoration='manual';if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('pageshow',restoreScroll);window.addEventListener('load',()=>{{restoreScroll();document.querySelectorAll('.pdf-link,.pdf-download').forEach((a)=>a.addEventListener('click',saveScroll));const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});window.addEventListener('beforeunload',saveScroll);</script></main></body></html>''')\n"}]}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":714,"output":1483,"cacheRead":28672,"cacheWrite":0,"totalTokens":30869,"cost":{"input":0.0017850000000000001,"output":0.022245,"cacheRead":0.007168,"cacheWrite":0,"total":0.031198000000000004}},"stopReason":"toolUse","timestamp":1780216429694,"responseId":"resp_0f029265420fe376016a1bf26dc8388191bc21adf63930b682"}}
{"type":"message","id":"ec752412","parentId":"4a3e524c","timestamp":"2026-05-31T08:34:18.434Z","message":{"role":"toolResult","toolCallId":"call_nTkZz5C6zA54BpnnHd2oMkXI|fc_0f029265420fe376016a1bf26fb1248191bf3ad161f7da3be7","toolName":"edit","content":[{"type":"text","text":"Successfully replaced 2 block(s) in /home/sebas/work/projects/rss-offline/build.py."}],"details":{"diff":"     ...\n 489 \n 490     pdf_archive = list_pdf_archive()\n 491     pdf_cards = []\n 492     for item in pdf_archive:\n-493         pdf_cards.append(f'''<article class=\"item\"><h2><a class=\"pdf-link\" href=\"{escape(item['path'])}\">{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n+493         pdf_cards.append(f'''<article class=\"item\"><h2><a class=\"pdf-link\" href=\"{escape(item['path'])}\">{escape(item['name'])}</a> <a class=\"pdf-download\" href=\"{escape(item['path'])}\" download>descargar</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n 494 \n 495     local_paths = [entry['local_path'] for entry in archived]\n-496     index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:visited{{color:#c69cff}}a:hover{{text-decoration:underline}}.pdf-link:visited{{color:#d9b8ff}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n+496     index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem;display:flex;gap:10px;align-items:center;flex-wrap:wrap}}a{{color:#8dc3ff;text-decoration:none}}a:visited{{color:#c69cff}}a:hover{{text-decoration:underline}}.pdf-link:visited{{color:#d9b8ff}}.pdf-download{{font-size:.78rem;padding:4px 8px;border:1px solid rgba(141,195,255,.35);border-radius:999px}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};const RSS_OFFLINE_SCROLL_KEY='rss-offline:index-scroll';const restoreScroll=()=>{{try{{const y=Number(sessionStorage.getItem(RSS_OFFLINE_SCROLL_KEY)||'0');if(y>0)requestAnimationFrame(()=>window.scrollTo(0,y));}}catch(_e){{}}}};const saveScroll=()=>{{try{{sessionStorage.setItem(RSS_OFFLINE_SCROLL_KEY,String(window.scrollY||window.pageYOffset||0));}}catch(_e){{}}}};if('scrollRestoration' in history)history.scrollRestoration='manual';if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('pageshow',restoreScroll);window.addEventListener('load',()=>{{restoreScroll();document.querySelectorAll('.pdf-link,.pdf-download').forEach((a)=>a.addEventListener('click',saveScroll));const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});window.addEventListener('beforeunload',saveScroll);</script></main></body></html>''')\n 497 \n 498     offline.write_text('<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>rss-offline sin conexión</title><style>body{font-family:system-ui,sans-serif;background:#07111f;color:#fff;display:grid;place-items:center;min-height:100vh;padding:24px;margin:0}main{max-width:520px;padding:24px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.05)}a{color:#8dc3ff}</style></head><body><main><h1>Sin conexión</h1><p>La lista y copias locales visitadas deberían seguir disponibles.</p><p><a href=\"/rss-offline/\">Volver a rss-offline</a></p></main></body></html>')\n 499 \n 500     precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + ([pdf_info['path']] if pdf_info else []) + [entry['local_path'] for entry in archived]\n     ...","patch":"--- /home/sebas/work/projects/rss-offline/build.py\n+++ /home/sebas/work/projects/rss-offline/build.py\n@@ -489,12 +489,12 @@\n \n     pdf_archive = list_pdf_archive()\n     pdf_cards = []\n     for item in pdf_archive:\n-        pdf_cards.append(f'''<article class=\"item\"><h2><a class=\"pdf-link\" href=\"{escape(item['path'])}\">{escape(item['name'])}</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n+        pdf_cards.append(f'''<article class=\"item\"><h2><a class=\"pdf-link\" href=\"{escape(item['path'])}\">{escape(item['name'])}</a> <a class=\"pdf-download\" href=\"{escape(item['path'])}\" download>descargar</a></h2><p class=\"meta\">{escape(item['date_label'])} · {item['size_mb']} MB</p></article>''')\n \n     local_paths = [entry['local_path'] for entry in archived]\n-    index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem}}a{{color:#8dc3ff;text-decoration:none}}a:visited{{color:#c69cff}}a:hover{{text-decoration:underline}}.pdf-link:visited{{color:#d9b8ff}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('load',()=>{{const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});</script></main></body></html>''')\n+    index.write_text(f'''<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"theme-color\" content=\"#07111f\"><title>rss-offline</title><style>*{{box-sizing:border-box}}body{{margin:0;font-family:system-ui,sans-serif;background:#07111f;color:#fff}}main{{max-width:960px;margin:0 auto;padding:24px}}h1,h2{{margin:0 0 8px}}.muted{{color:rgba(255,255,255,.68)}}.item{{padding:16px 0;border-bottom:1px solid rgba(255,255,255,.08)}}.item h2{{margin:0 0 6px;font-size:1.1rem;display:flex;gap:10px;align-items:center;flex-wrap:wrap}}a{{color:#8dc3ff;text-decoration:none}}a:visited{{color:#c69cff}}a:hover{{text-decoration:underline}}.pdf-link:visited{{color:#d9b8ff}}.pdf-download{{font-size:.78rem;padding:4px 8px;border:1px solid rgba(141,195,255,.35);border-radius:999px}}.meta{{font-size:.92rem;color:rgba(255,255,255,.72)}} .top{{display:flex;justify-content:space-between;gap:12px;align-items:end;flex-wrap:wrap;margin-bottom:18px}}section{{margin-top:28px}}</style></head><body><main><div class=\"top\"><div><h1>rss-offline</h1><p class=\"muted\">PDF diario con artículos nuevos.</p></div><p class=\"muted\">{len(archived)} artículos · últimos {DAYS} días</p></div><section><h2>Archivo de PDFs</h2>{''.join(pdf_cards) or '<p class=\"muted\">Todavía no hay PDFs.</p>'}</section><script>const RSS_OFFLINE_LOCAL_PATHS={json.dumps(local_paths, ensure_ascii=False)};const RSS_OFFLINE_SCROLL_KEY='rss-offline:index-scroll';const restoreScroll=()=>{{try{{const y=Number(sessionStorage.getItem(RSS_OFFLINE_SCROLL_KEY)||'0');if(y>0)requestAnimationFrame(()=>window.scrollTo(0,y));}}catch(_e){{}}}};const saveScroll=()=>{{try{{sessionStorage.setItem(RSS_OFFLINE_SCROLL_KEY,String(window.scrollY||window.pageYOffset||0));}}catch(_e){{}}}};if('scrollRestoration' in history)history.scrollRestoration='manual';if('serviceWorker' in navigator){{window.addEventListener('load',()=>navigator.serviceWorker.register('/rss-offline/sw.js').catch(()=>{{}}));}}window.addEventListener('pageshow',restoreScroll);window.addEventListener('load',()=>{{restoreScroll();document.querySelectorAll('.pdf-link,.pdf-download').forEach((a)=>a.addEventListener('click',saveScroll));const warm=async()=>{{for(const path of RSS_OFFLINE_LOCAL_PATHS){{try{{await fetch(path,{{cache:'force-cache'}});}}catch(_e){{}}}}}};setTimeout(warm,1200);}});window.addEventListener('beforeunload',saveScroll);</script></main></body></html>''')\n \n     offline.write_text('<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>rss-offline sin conexión</title><style>body{font-family:system-ui,sans-serif;background:#07111f;color:#fff;display:grid;place-items:center;min-height:100vh;padding:24px;margin:0}main{max-width:520px;padding:24px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(255,255,255,.05)}a{color:#8dc3ff}</style></head><body><main><h1>Sin conexión</h1><p>La lista y copias locales visitadas deberían seguir disponibles.</p><p><a href=\"/rss-offline/\">Volver a rss-offline</a></p></main></body></html>')\n \n     precache_paths = ['/rss-offline/', '/rss-offline/offline.html', '/rss-offline/entries.json'] + ([pdf_info['path']] if pdf_info else []) + [entry['local_path'] for entry in archived]\n","firstChangedLine":493},"isError":false,"timestamp":1780216458434}}
{"type":"message","id":"ea5fbfbb","parentId":"ec752412","timestamp":"2026-05-31T08:34:24.265Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_2AIavTotv4BGNUA42fWYDjQo|fc_0f029265420fe376016a1bf28dd15c8191bcbc2a4819088fce","name":"bash","arguments":{"command":"cd /home/sebas/work/projects/rss-offline && sudo ./build.py >/tmp/rss-offline-build.out && python - <<'PY'\nimport requests\nidx=requests.get('https://ballbox-first.emperor-ratio.ts.net/rss-offline/',timeout=20).text\nchecks={\n 'download_button':'pdf-download' in idx and '>descargar<' in idx,\n 'scroll_key':'rss-offline:index-scroll' in idx,\n 'manual_scroll_restoration':'history.scrollRestoration' in idx,\n}\nprint(checks)\nPY","timeout":900}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1715,"output":139,"cacheRead":29184,"cacheWrite":0,"totalTokens":31038,"cost":{"input":0.0042875000000000005,"output":0.002085,"cacheRead":0.0072959999999999995,"cacheWrite":0,"total":0.0136685}},"stopReason":"toolUse","timestamp":1780216458435,"responseId":"resp_0f029265420fe376016a1bf28a7fc0819188dc3270316bbc12"}}
{"type":"message","id":"186ed289","parentId":"ea5fbfbb","timestamp":"2026-05-31T08:37:02.394Z","message":{"role":"toolResult","toolCallId":"call_2AIavTotv4BGNUA42fWYDjQo|fc_0f029265420fe376016a1bf28dd15c8191bcbc2a4819088fce","toolName":"bash","content":[{"type":"text","text":"{'download_button': True, 'scroll_key': True, 'manual_scroll_restoration': True}\n"}],"isError":false,"timestamp":1780216622394}}
{"type":"message","id":"96d80526","parentId":"186ed289","timestamp":"2026-05-31T08:37:03.899Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_AbTQqqqFiQf4iSfncWFNW8ME|fc_0f029265420fe376016a1bf32f7a3c8191b93ce49ab32d8943","name":"todo","arguments":{"action":"update","id":2,"status":"completed"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":31069,"output":25,"cacheRead":0,"cacheWrite":0,"totalTokens":31094,"cost":{"input":0.0776725,"output":0.000375,"cacheRead":0,"cacheWrite":0,"total":0.0780475}},"stopReason":"toolUse","timestamp":1780216622395,"responseId":"resp_0f029265420fe376016a1bf32e7b6c819182e07fceb3c89ed5"}}
{"type":"message","id":"ac82ca78","parentId":"96d80526","timestamp":"2026-05-31T08:37:03.900Z","message":{"role":"toolResult","toolCallId":"call_AbTQqqqFiQf4iSfncWFNW8ME|fc_0f029265420fe376016a1bf32f7a3c8191b93ce49ab32d8943","toolName":"todo","content":[{"type":"text","text":"Updated #2 (pending → completed)"}],"details":{"action":"update","params":{"action":"update","id":2,"status":"completed"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"completed","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"}],"nextId":3},"isError":false,"timestamp":1780216623900}}
{"type":"message","id":"a6f8885a","parentId":"ac82ca78","timestamp":"2026-05-31T08:37:07.815Z","message":{"role":"assistant","content":[{"type":"text","text":"Sí.\n\nHecho:\n- link principal del PDF:\n  - abre normal\n  - mantiene `:visited`\n- botón al lado:\n  - `descargar`\n  - baja directo el PDF\n- scroll position:\n  - guardado en `sessionStorage`\n  - se restaura al volver a la lista\n  - forzado también en `pageshow`\n\nURL:\n- https://ballbox-first.emperor-ratio.ts.net/rss-offline/\n\nSobre “se puede?”:\n- sí, para volver a la lista en la misma pestaña\n- depende un poco del visor PDF/browser, pero del lado HTML ya quedó cubierto bastante bien\n\nSi querés, siguiente mejora:\n- que el PDF se abra en pestaña nueva\n- y así la lista ni se mueve ni se recarga","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1bf330cb4081919bf8991fa6b9826a\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":392,"output":167,"cacheRead":30720,"cacheWrite":0,"totalTokens":31279,"cost":{"input":0.0009800000000000002,"output":0.002505,"cacheRead":0.007679999999999999,"cacheWrite":0,"total":0.011165}},"stopReason":"stop","timestamp":1780216623901,"responseId":"resp_0f029265420fe376016a1bf32ffbf8819180280006ee0f4ffb"}}
{"type":"message","id":"67e3f7d8","parentId":"a6f8885a","timestamp":"2026-05-31T09:25:15.718Z","message":{"role":"user","content":[{"type":"text","text":"[telegram] ahora indaga los ultimos pdfs e intenta en el builder targetear mejor los blogs y nada mas (github y digitalocean en particular tienen mucho extra)\nno hace falta que rebuildees nada"}],"timestamp":1780219515717}}
{"type":"message","id":"4854deed","parentId":"67e3f7d8","timestamp":"2026-05-31T09:25:20.538Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Inspecting build rules**\n\nI need to check the build rules and consider proposing edits. The user has asked me to look into the latest PDFs and try to improve the blogs without needing a rebuild. I think I should check the generated HTML from specific sources and create a to-do list for that. I can edit build.py without triggering a rebuild, so I might also look at the locally generated article print HTML for GitHub or DigitalOcean.","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bfe7d842c8191b8b1ee1680959d58\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_6AmNN7dfNjvY9QhZuIgCO9-bTqOUEN1IXgVclKVwlTPLuOzbXGfxtWxysGRxUAs6ug-jLKSugBXUWpBSiIDshUGn24jvRtoQNUrlukxBdmqx9O4gysQ64F1uQi49YUyoCjUQPYDBaUgVMtk_a4XE-lhTtR9l0OzMCii94M_6CjIvYTuXNM8aPSZ-0MvWk0bgGqeQS8wi4TJmVFu-zfYaO7VjzMTZS2ZGHII09nbCtwmba5-M-NoODdkskPZ1II71TWqAbax_shphStBaq3FQgMcEGmPrzhbjoH2XbSaR4Z1WF1PzCesf-XQisjkh3xWSwVpZV_auGpR_nkQYkdaJNYCkAzKHJIPr02xCX9LjGQg2--G1KxncyM4fiWNiCGADbiZcABt3TCvoOW7K8HW_gw9N0svZS3EW5DoewnuBuz2ASL_GSc5GsiGY54M452OGZoAMAu_PdATJTKIjihn_pRvzZGEcRQApNgo3HuOkUNlBsg7fmnAKk1O2O_COXbanZsqaRGdud38B78rsY6XVJKFMF5FYkHm-duBBNk3Ajj-bBRzUECe826LkfFLJZWxE3kWHT90swO6XpyNvxxT2pWdpTsfvJkOp_UW6CPG5aM8Xi0gW1LkZt416r3Mheehhy7_S1oMD1tEu08E-Qa-qgNvH0VTe4DeiXbvgVCrd1i8qzI26ih3xGXJpDqkcrElQvY_UfEX8Wxk0SFIzwjLBL2Mqa0AQvadd6Tc6Clwf-EXC2G0PI81atscp64L8nwBkX2a8Rv2qkUyNTLDEvidQJZ2M_84uKh5jAbLSLgEM8n-zbcBUMPm8w6tNYmJjUwLqJjLiaySTgiWcGL_3kQBUgVvXyMB_sAHda3Hj6B_t2aoEP14KaftitEf7TKF-3Sp6JS2lS0f2nAI5OFwIM2e3A10tGydfYaYJNCl6sfHaik3V2kPrzqAm_7zsf6ze7xDdgxxdR2454DXzn5cqXKbG93qMD5f9uSg4GeChaUvufdshf39giI-Cs38Awm9tuqcRbwTEjkyuqLfLe4SqIAWz7dEbYsnrnGGsPua1hwXvkJKFObhXmCsUkuBC9l0BLUWPJMveZPMmSVd1-D75x63k7YZ7JwMEXsHG1Us_nkooL2oYPt9hbuge5tNJ8YAYYt7Lp1sdrVGgF18urD1RODYrnA5EK6LvRvfBA-JVpV0ajiT19FJ246zPTCJtYGyZNLKPO3Cq2zii1pHPWO3F2i2bdiFt8GRESJffAaEl-efU4jrDQZpgVAYz2aC1G4iXuOspLDDQKLXm-1qIeFQTyhpXkX2Jncj3C0CVInT4WoEKf8HnccYleYoYRSslzyxuT3u-KnWN8w_tGmDaJfd_8IZe35RKsyPS5CW6_7ncSo46Fi_TM=\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Inspecting build rules**\\n\\nI need to check the build rules and consider proposing edits. The user has asked me to look into the latest PDFs and try to improve the blogs without needing a rebuild. I think I should check the generated HTML from specific sources and create a to-do list for that. I can edit build.py without triggering a rebuild, so I might also look at the locally generated article print HTML for GitHub or DigitalOcean.\"}]}"},{"type":"toolCall","id":"call_dCmKnneswNXM7NScvnkPLp3X|fc_0f029265420fe376016a1bfe8046408191b0d5f14f44c588f9","name":"todo","arguments":{"action":"create","subject":"Tighten rss-offline PDF extraction for noisy blogs","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","status":"in_progress","owner":"agent"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":31233,"output":159,"cacheRead":0,"cacheWrite":0,"totalTokens":31392,"cost":{"input":0.07808250000000001,"output":0.002385,"cacheRead":0,"cacheWrite":0,"total":0.08046750000000001}},"stopReason":"toolUse","timestamp":1780219515719,"responseId":"resp_0f029265420fe376016a1bfe7c82c481919b922e174875c42c"}}
{"type":"message","id":"d4c5bd52","parentId":"4854deed","timestamp":"2026-05-31T09:25:20.539Z","message":{"role":"toolResult","toolCallId":"call_dCmKnneswNXM7NScvnkPLp3X|fc_0f029265420fe376016a1bfe8046408191b0d5f14f44c588f9","toolName":"todo","content":[{"type":"text","text":"Created #3: Tighten rss-offline PDF extraction for noisy blogs (pending)"}],"details":{"action":"create","params":{"action":"create","subject":"Tighten rss-offline PDF extraction for noisy blogs","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","status":"in_progress","owner":"agent"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"completed","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"},{"id":3,"subject":"Tighten rss-offline PDF extraction for noisy blogs","status":"pending","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","owner":"agent"}],"nextId":4},"isError":false,"timestamp":1780219520539}}
{"type":"message","id":"256747b1","parentId":"d4c5bd52","timestamp":"2026-05-31T09:25:23.285Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_R6djNO611gS12k2HhT6kS2re|fc_0f029265420fe376016a1bfe833d1081919bc50d5df2445bf3","name":"bash","arguments":{"command":"find /var/www/rss-offline/pdf -maxdepth 1 -type f | sort | tail -20","timeout":10}},{"type":"toolCall","id":"call_DG4NE0PAHwEmnnTV9G2hjNN9|fc_0f029265420fe376016a1bfe833d208191b46a0f99a35cb059","name":"bash","arguments":{"command":"ls -1 /var/www/rss-offline/article-*.html | tail -20","timeout":10}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":699,"output":93,"cacheRead":30720,"cacheWrite":0,"totalTokens":31512,"cost":{"input":0.0017475000000000001,"output":0.001395,"cacheRead":0.007679999999999999,"cacheWrite":0,"total":0.010822499999999999}},"stopReason":"toolUse","timestamp":1780219520540,"responseId":"resp_0f029265420fe376016a1bfe8099cc81918ca8391d4791c07a"}}
{"type":"message","id":"d6f7edf8","parentId":"256747b1","timestamp":"2026-05-31T09:25:23.302Z","message":{"role":"toolResult","toolCallId":"call_R6djNO611gS12k2HhT6kS2re|fc_0f029265420fe376016a1bfe833d1081919bc50d5df2445bf3","toolName":"bash","content":[{"type":"text","text":"/var/www/rss-offline/pdf/article-897d93c50ad53a6e.print.html\n/var/www/rss-offline/pdf/article-8ce8fe6b9afb1b1c.print.html\n/var/www/rss-offline/pdf/article-8d54ec23dc63c0cf.print.html\n/var/www/rss-offline/pdf/article-98558fbdd89c6b67.print.html\n/var/www/rss-offline/pdf/article-b1de76efb323ff82.print.html\n/var/www/rss-offline/pdf/article-b2f3c4608b954429.print.html\n/var/www/rss-offline/pdf/article-b5ddc6629f6a7c2d.print.html\n/var/www/rss-offline/pdf/article-b88b0610b56ab9fb.print.html\n/var/www/rss-offline/pdf/article-bb51898f223204ef.print.html\n/var/www/rss-offline/pdf/article-bc93d7b33addd1e2.print.html\n/var/www/rss-offline/pdf/article-c05e51dd412952e4.print.html\n/var/www/rss-offline/pdf/article-c1ac4d327109cf09.print.html\n/var/www/rss-offline/pdf/article-c22ede05fdcde865.print.html\n/var/www/rss-offline/pdf/article-c49c56b7ed458190.print.html\n/var/www/rss-offline/pdf/article-c6f41170288b1b07.print.html\n/var/www/rss-offline/pdf/article-c9dde27b9de5a145.print.html\n/var/www/rss-offline/pdf/article-d44db85543e860b6.print.html\n/var/www/rss-offline/pdf/article-e730d9b21ea1c9bb.print.html\n/var/www/rss-offline/pdf/article-f0c570917d1b9416.print.html\n/var/www/rss-offline/pdf/article-f419e05b4804e437.print.html\n"}],"isError":false,"timestamp":1780219523302}}
{"type":"message","id":"d4f68b11","parentId":"d6f7edf8","timestamp":"2026-05-31T09:25:23.302Z","message":{"role":"toolResult","toolCallId":"call_DG4NE0PAHwEmnnTV9G2hjNN9|fc_0f029265420fe376016a1bfe833d208191b46a0f99a35cb059","toolName":"bash","content":[{"type":"text","text":"/var/www/rss-offline/article-baf0560bfe83e07a.html\n/var/www/rss-offline/article-bb51898f223204ef.html\n/var/www/rss-offline/article-bc93d7b33addd1e2.html\n/var/www/rss-offline/article-be577c6425356c70.html\n/var/www/rss-offline/article-c05e51dd412952e4.html\n/var/www/rss-offline/article-c1ac4d327109cf09.html\n/var/www/rss-offline/article-c22ede05fdcde865.html\n/var/www/rss-offline/article-c49c56b7ed458190.html\n/var/www/rss-offline/article-c6f41170288b1b07.html\n/var/www/rss-offline/article-c858592a149e5a01.html\n/var/www/rss-offline/article-c9dde27b9de5a145.html\n/var/www/rss-offline/article-cafc54395b674132.html\n/var/www/rss-offline/article-cbebf207f4772426.html\n/var/www/rss-offline/article-d44db85543e860b6.html\n/var/www/rss-offline/article-e730d9b21ea1c9bb.html\n/var/www/rss-offline/article-ee31b1556857165d.html\n/var/www/rss-offline/article-f0c570917d1b9416.html\n/var/www/rss-offline/article-f419e05b4804e437.html\n/var/www/rss-offline/article-f8d49f800ade15be.html\n/var/www/rss-offline/article-fc2d142f0379dac3.html\n"}],"isError":false,"timestamp":1780219523302}}
{"type":"message","id":"1c69e9f2","parentId":"d4f68b11","timestamp":"2026-05-31T09:25:26.661Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Mapping slugs to links**\n\nAlright, I need to think about mapping slugs to links. It seems like I might need to reference something like entries.json for that. This could help me organize and connect the slugs properly to their corresponding links. I imagine simplifying the process if I get that mapping down. So my first step will be checking entries.json to see what I can find for this task. Let's figure this out!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bfe8435748191ad5278ecd20a5816\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_6Fygn9eXWSMjWVfIDbCn0pwG0_qXc7JLXrlsWAWZigN749Xy5zKIpjkDe73jHOilyaIDD8ROGFCXTVmB6iWRvBFuyxQy6EgR8HHsmIkg0F45KjuKsXbn8AEKfC95u7owJBmlvmfT38jiC6JYV0MfiMPNsfvML3jxtuNgJ08hQ5l2iUBJwnL1tRMK6jrCdXtytzbhZjnm3ZxyDk1OMk55Mud7Ie1FPsYi5aLiIJsIoGFbMgYl2e6CeBE2zCR9ec8dqyy81FoAXWYsrqemEEBae_FLTkdQZl2Ab7ZnSHF5PYSl2RCzRF6-5qEK2weVMQvtlzzBOpf7NaFapnF_exearLqa15txClkAQRt4Xn7yUuOiqKaL0r7EgKQCbXJ6Z4O8uthLL0h3BmEA3LnawjSpDq4nRh0CeBuD4lfYBow8bS7JLwC8j64XdBrB1nlOeN6kX-OA1VOeqrKs2Zxk_-B65VlxO0z11-8Wv2HkUBBcgXHbWXU5ovTfYrBsNGkp9pPQF1OR0Aj0ybZ73NspmmcoV9mkjmq1D1jstAUTYdkujo9Juvn6TuTD4W-lRufpsbO7sj0Jw1kuWwQtkw90OSqzQlIC6hhsGINmjuV7JJX6DeSeyJUXOh7Uxe1igkM-mP8w7xFHGIQeH_0TnQI08BxaBnjpffPqd-rDWhnibgT1-6QroJ55W9tKKE2gxC1H9EG93ZZSDYx6AXj9zg2hqA4x7occJ-EESpY7jC6D-q08X7oo5WTotlkQys32ICVYfawbcJk-luRb421Ts-otjaIPW9so9WO_Dr5LkvPUkVyAZ1LlcEZ3HvDykY2XOllFuL2dLRW3Uhi7T6xmy_9b9jWtQ026EqPLnOw609TsnoxhomEVp7MClYihZ_jSc_u_xqedUin7tuzkzazmeJN34a8Heb2Zng_sVRggtZjgVJpyxrEJ6koDuJioPEWe8rUJitFSnRbmQW9LT5jfYPQFi82iK6Ig==\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Mapping slugs to links**\\n\\nAlright, I need to think about mapping slugs to links. It seems like I might need to reference something like entries.json for that. This could help me organize and connect the slugs properly to their corresponding links. I imagine simplifying the process if I get that mapping down. So my first step will be checking entries.json to see what I can find for this task. Let's figure this out!\"}]}"},{"type":"toolCall","id":"call_UUnqMr4k1iCOB22NZqrDrcVp|fc_0f029265420fe376016a1bfe858ed881918fabad030a90ba62","name":"bash","arguments":{"command":"python - <<'PY'\nimport json\nfrom pathlib import Path\nentries=json.loads(Path('/var/www/rss-offline/entries.json').read_text())\nfor e in entries[:20]:\n    if 'github.blog' in e['link'] or 'digitalocean.com/blog' in e['link']:\n        print(e['local_path'].split('article-')[-1].split('.html')[0], e['link'])\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1085,"output":134,"cacheRead":31232,"cacheWrite":0,"totalTokens":32451,"cost":{"input":0.0027125,"output":0.00201,"cacheRead":0.007808,"cacheWrite":0,"total":0.0125305}},"stopReason":"toolUse","timestamp":1780219523303,"responseId":"resp_0f029265420fe376016a1bfe835d3c819196003601ecae8d3a"}}
{"type":"message","id":"90b5fcc1","parentId":"1c69e9f2","timestamp":"2026-05-31T09:25:26.722Z","message":{"role":"toolResult","toolCallId":"call_UUnqMr4k1iCOB22NZqrDrcVp|fc_0f029265420fe376016a1bfe858ed881918fabad030a90ba62","toolName":"bash","content":[{"type":"text","text":"b5ddc6629f6a7c2d https://www.digitalocean.com/blog/ai-disruptors\nc22ede05fdcde865 https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\nc05e51dd412952e4 https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\nc6f41170288b1b07 https://www.digitalocean.com/blog/introducing-batch-inference\n98558fbdd89c6b67 https://github.blog/developer-skills/github/github-for-beginners-getting-started-with-git-and-github-in-vs-code/\n"}],"isError":false,"timestamp":1780219526721}}
{"type":"message","id":"2da4edfa","parentId":"90b5fcc1","timestamp":"2026-05-31T09:25:31.813Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_3Qgy3YW5VNlnDwyJkABNaGDz|fc_0f029265420fe376016a1bfe8bab188191b38ad2075f8d04ef","name":"read","arguments":{"path":"/var/www/rss-offline/article-c05e51dd412952e4.html","offset":1,"limit":220}},{"type":"toolCall","id":"call_5Vx1dcNiLFATM3JbQmcGJMrD|fc_0f029265420fe376016a1bfe8bab288191b0882eab7a7a122e","name":"read","arguments":{"path":"/var/www/rss-offline/pdf/article-c05e51dd412952e4.print.html","offset":1,"limit":220}},{"type":"toolCall","id":"call_stXwYgiqOumhut6MNi1OylI1|fc_0f029265420fe376016a1bfe8bab30819182d85ef66f12cf72","name":"read","arguments":{"path":"/var/www/rss-offline/article-c22ede05fdcde865.html","offset":1,"limit":220}},{"type":"toolCall","id":"call_iOIFuUQLnIPODEsVx1l8Wf5d|fc_0f029265420fe376016a1bfe8bab348191a15e2bb7cc7063f5","name":"read","arguments":{"path":"/var/www/rss-offline/pdf/article-c22ede05fdcde865.print.html","offset":1,"limit":220}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":876,"output":170,"cacheRead":31744,"cacheWrite":0,"totalTokens":32790,"cost":{"input":0.00219,"output":0.00255,"cacheRead":0.007936,"cacheWrite":0,"total":0.012676}},"stopReason":"toolUse","timestamp":1780219526723,"responseId":"resp_0f029265420fe376016a1bfe86c98c81919e819f93e9245508"}}
{"type":"message","id":"4abf6e13","parentId":"2da4edfa","timestamp":"2026-05-31T09:25:31.843Z","message":{"role":"toolResult","toolCallId":"call_3Qgy3YW5VNlnDwyJkABNaGDz|fc_0f029265420fe376016a1bfe8bab188191b38ad2075f8d04ef","toolName":"read","content":[{"type":"text","text":"<!doctype html>\n<!DOCTYPE html>\n\n<html class=\"mt-0\" lang=\"en-US\" style=\"--navigation-offset: 0px; --header-offset: 75px;\"><head><base href=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\"/>\n<meta charset=\"utf-8\"/>\n<meta content=\"width=device-width, initial-scale=1\" name=\"viewport\"/>\n<link href=\"https://gmpg.org/xfn/11\" rel=\"profile\"/>\n<link href=\"https://github.githubassets.com/favicon.ico\" rel=\"icon\" type=\"image/x-icon\"/>\n<meta content=\"index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1\" name=\"robots\"/>\n<!-- This site is optimized with the Yoast SEO Premium plugin v27.5 (Yoast SEO v27.5) - https://yoast.com/product/yoast-seo-premium-wordpress/ -->\n<title>Still a developer. Just outside. Our latest GitHub Shop collection is here. - The GitHub Blog</title>\n<meta content=\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\" name=\"description\"/>\n<link href=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\" rel=\"canonical\"/>\n<meta content=\"en_US\" property=\"og:locale\"/>\n<meta content=\"article\" property=\"og:type\"/>\n<meta content=\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\" property=\"og:title\"/>\n<meta content=\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\" property=\"og:description\"/>\n<meta content=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\" property=\"og:url\"/>\n<meta content=\"The GitHub Blog\" property=\"og:site_name\"/>\n<meta content=\"2026-05-28T18:18:43+00:00\" property=\"article:published_time\"/>\n<meta content=\"2026-05-28T18:18:46+00:00\" property=\"article:modified_time\"/>\n<meta content=\"https://github.blog/wp-content/uploads/2026/05/Blog-Header.png\" property=\"og:image\"/>\n<meta content=\"2286\" property=\"og:image:width\"/>\n<meta content=\"1210\" property=\"og:image:height\"/>\n<meta content=\"image/png\" property=\"og:image:type\"/>\n<meta content=\"Lavinia Sfetcu\" name=\"author\"/>\n<meta content=\"summary_large_image\" name=\"twitter:card\"/>\n<meta content=\"Written by\" name=\"twitter:label1\"/>\n<meta content=\"Lavinia Sfetcu\" name=\"twitter:data1\"/>\n<meta content=\"Est. reading time\" name=\"twitter:label2\"/>\n<meta content=\"3 minutes\" name=\"twitter:data2\"/>\n<script type=\"application/ld+json\">{\"@context\":\"https:\\/\\/schema.org\",\"headline\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\",\"author\":{\"@type\":\"Person\",\"name\":\"Natalie Guevara\"},\"datePublished\":\"2026-05-28T11:18:43-07:00\",\"abstract\":\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\",\"mainEntityOfPage\":{\"@type\":\"WebPage\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"},\"image\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?resize=150%2C150\",\"@type\":\"TechArticle\"}</script>\n<script type=\"application/ld+json\">{\"@context\":\"https:\\/\\/schema.org\",\"headline\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\",\"author\":{\"@type\":\"Person\",\"name\":\"Natalie Guevara\"},\"datePublished\":\"2026-05-28T11:18:43-07:00\",\"abstract\":\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\",\"mainEntityOfPage\":{\"@type\":\"WebPage\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"},\"image\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?resize=150%2C150\",\"@type\":\"BlogPosting\"}</script>\n<script class=\"yoast-schema-graph\" type=\"application/ld+json\">{\"@context\":\"https:\\/\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#article\",\"isPartOf\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"},\"author\":[{\"@id\":\"https:\\/\\/github.blog\\/#\\/schema\\/person\\/5e0ebc47a163812542fb84009a1eadac\"}],\"headline\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\",\"datePublished\":\"2026-05-28T18:18:43+00:00\",\"dateModified\":\"2026-05-28T18:18:46+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"},\"wordCount\":423,\"image\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?fit=2286%2C1210\",\"keywords\":[\"GitHub Shop\"],\"articleSection\":[\"Company news\",\"News &amp; insights\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\",\"url\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\",\"name\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here. - The GitHub Blog\",\"isPartOf\":{\"@id\":\"https:\\/\\/github.blog\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?fit=2286%2C1210\",\"datePublished\":\"2026-05-28T18:18:43+00:00\",\"dateModified\":\"2026-05-28T18:18:46+00:00\",\"author\":{\"@id\":\"https:\\/\\/github.blog\\/#\\/schema\\/person\\/37273876e05b418f9e59da3267030711\"},\"description\":\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\",\"breadcrumb\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#primaryimage\",\"url\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?fit=2286%2C1210\",\"contentUrl\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?fit=2286%2C1210\",\"width\":2286,\"height\":1210,\"caption\":\"Items from the new ESC collection at the GitHub Shop are featured in front of a colorful background. Items include a hoodie coozie, a tee shirt, and a Cabana set of matching shirt and shorts.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\/\\/github.blog\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"News &amp; insights\",\"item\":\"https:\\/\\/github.blog\\/news-insights\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Company news\",\"item\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\/\\/github.blog\\/#website\",\"url\":\"https:\\/\\/github.blog\\/\",\"name\":\"The GitHub Blog\",\"description\":\"Updates, ideas, and inspiration from GitHub to help developers build and design software.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\/\\/github.blog\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\/\\/github.blog\\/#\\/schema\\/person\\/5e0ebc47a163812542fb84009a1eadac\",\"name\":\"Lavinia Sfetcu\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\/\\/secure.gravatar.com\\/avatar\\/294ad6be270da24048c247d3ef65839df645a862e46ff6547dd2ae605f4aa437?s=96&d=mm&r=ge545d325921a67df965e10ee1f12a9d1\",\"url\":\"https:\\/\\/secure.gravatar.com\\/avatar\\/294ad6be270da24048c247d3ef65839df645a862e46ff6547dd2ae605f4aa437?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\/\\/secure.gravatar.com\\/avatar\\/294ad6be270da24048c247d3ef65839df645a862e46ff6547dd2ae605f4aa437?s=96&d=mm&r=g\",\"caption\":\"Lavinia Sfetcu\"},\"url\":\"https:\\/\\/github.blog\\/author\\/laviniasf\\/\"}]}</script>\n<!-- / Yoast SEO Premium plugin. -->\n<link href=\"//ghcc.githubassets.com\" rel=\"dns-prefetch\"/>\n<link href=\"//js.monitor.azure.com\" rel=\"dns-prefetch\"/>\n<link href=\"//cdnjs.cloudflare.com\" rel=\"dns-prefetch\"/>\n<link href=\"//analytics.githubassets.com\" rel=\"dns-prefetch\"/>\n<link href=\"//stats.wp.com\" rel=\"dns-prefetch\"/>\n<link href=\"//v0.wordpress.com\" rel=\"dns-prefetch\"/>\n<link href=\"https://github.blog/feed/\" rel=\"alternate\" title=\"The GitHub Blog » Feed\" type=\"application/rss+xml\"/>\n<link href=\"https://github.blog/comments/feed/\" rel=\"alternate\" title=\"The GitHub Blog » Comments Feed\" type=\"application/rss+xml\"/>\n<link href=\"https://github.blog/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fgithub.blog%2Fnews-insights%2Fcompany-news%2Fstill-a-developer-just-outside-our-latest-github-shop-collection-is-here%2F\" rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\"/>\n<link href=\"https://github.blog/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fgithub.blog%2Fnews-insights%2Fcompany-news%2Fstill-a-developer-just-outside-our-latest-github-shop-collection-is-here%2F&amp;format=xml\" rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\"/>\n<style id=\"wp-img-auto-sizes-contain-inline-css\">\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id=\"wp-emoji-styles-inline-css\">\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id=\"wp-block-library-inline-css\">\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}.has-text-align-center{text-align:center}.has-text-align-left{text-align:left}.has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;word-wrap:normal!important}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style>\n<style id=\"wp-block-button-inline-css\">\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://github.blog/wp-includes/blocks/button/style.min.css */\n</style>\n<style id=\"wp-block-heading-inline-css\">\nh1:where(.wp-block-heading).has-background,h2:where(.wp-block-heading).has-background,h3:where(.wp-block-heading).has-background,h4:where(.wp-block-heading).has-background,h5:where(.wp-block-heading).has-background,h6:where(.wp-block-heading).has-background{padding:1.25em 2.375em}h1.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h1.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h2.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h2.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h3.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h3.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h4.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h4.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h5.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h5.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h6.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h6.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]){rotate:180deg}\n/*# sourceURL=https://github.blog/wp-includes/blocks/heading/style.min.css */\n</style>\n<style id=\"wp-block-image-inline-css\">\n.wp-block-image>a,.wp-block-image>figure>a{display:inline-block}.wp-block-image img{box-sizing:border-box;height:auto;max-width:100%;vertical-align:bottom}@media not (prefers-reduced-motion){.wp-block-image img.hide{visibility:hidden}.wp-block-image img.show{animation:show-content-image .4s}}.wp-block-image[style*=border-radius] img,.wp-block-image[style*=border-radius]>a{border-radius:inherit}.wp-block-image.has-custom-border img{box-sizing:border-box}.wp-block-image.aligncenter{text-align:center}.wp-block-image.alignfull>a,.wp-block-image.alignwide>a{width:100%}.wp-block-image.alignfull img,.wp-block-image.alignwide img{height:auto;width:100%}.wp-block-image .aligncenter,.wp-block-image .alignleft,.wp-block-image .alignright,.wp-block-image.aligncenter,.wp-block-image.alignleft,.wp-block-image.alignright{display:table}.wp-block-image .aligncenter>figcaption,.wp-block-image .alignleft>figcaption,.wp-block-image .alignright>figcaption,.wp-block-image.aligncenter>figcaption,.wp-block-image.alignleft>figcaption,.wp-block-image.alignright>figcaption{caption-side:bottom;display:table-caption}.wp-block-image .alignleft{float:left;margin:.5em 1em .5em 0}.wp-block-image .alignright{float:right;margin:.5em 0 .5em 1em}.wp-block-image .aligncenter{margin-left:auto;margin-right:auto}.wp-block-image :where(figcaption){margin-bottom:1em;margin-top:.5em}.wp-block-image.is-style-circle-mask img{border-radius:9999px}@supports ((-webkit-mask-image:none) or (mask-image:none)) or (-webkit-mask-image:none){.wp-block-image.is-style-circle-mask img{border-radius:0;-webkit-mask-image:url('data:image/svg+xml;utf8,<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>');mask-mode:alpha;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}}:root :where(.wp-block-image.is-style-rounded img,.wp-block-image .is-style-rounded img){border-radius:9999px}.wp-block-image figure{margin:0}.wp-lightbox-container{display:flex;flex-direction:column;position:relative}.wp-lightbox-container img{cursor:zoom-in}.wp-lightbox-container img:hover+button{opacity:1}.wp-lightbox-container button{align-items:center;backdrop-filter:blur(16px) saturate(180%);background-color:#5a5a5a40;border:none;border-radius:4px;cursor:zoom-in;display:flex;height:20px;justify-content:center;opacity:0;padding:0;position:absolute;right:16px;text-align:center;top:16px;width:20px;z-index:100}@media not (prefers-reduced-motion){.wp-lightbox-container button{transition:opacity .2s ease}}.wp-lightbox-container button:focus-visible{outline:3px auto #5a5a5a40;outline:3px auto -webkit-focus-ring-color;outline-offset:3px}.wp-lightbox-container button:hover{cursor:pointer;opacity:1}.wp-lightbox-container button:focus{opacity:1}.wp-lightbox-container button:focus,.wp-lightbox-container button:hover,.wp-lightbox-container button:not(:hover):not(:active):not(.has-background){background-color:#5a5a5a40;border:none}.wp-lightbox-overlay{box-sizing:border-box;cursor:zoom-out;height:100vh;left:0;overflow:hidden;position:fixed;top:0;visibility:hidden;width:100%;z-index:100000}.wp-lightbox-overlay .close-button{align-items:center;cursor:pointer;display:flex;justify-content:center;min-height:40px;min-width:40px;padding:0;position:absolute;right:calc(env(safe-area-inset-right) + 16px);top:calc(env(safe-area-inset-top) + 16px);z-index:5000000}.wp-lightbox-overlay .close-button:focus,.wp-lightbox-overlay .close-button:hover,.wp-lightbox-overlay .close-button:not(:hover):not(:active):not(.has-background){background:none;border:none}.wp-lightbox-overlay .lightbox-image-container{height:var(--wp--lightbox-container-height);left:50%;overflow:hidden;position:absolute;top:50%;transform:translate(-50%,-50%);transform-origin:top left;width:var(--wp--lightbox-container-width);z-index:9999999999}.wp-lightbox-overlay .wp-block-image{align-items:center;box-sizing:border-box;display:flex;height:100%;justify-content:center;margin:0;position:relative;transform-origin:0 0;width:100%;z-index:3000000}.wp-lightbox-overlay .wp-block-image img{height:var(--wp--lightbox-image-height);min-height:var(--wp--lightbox-image-height);min-width:var(--wp--lightbox-image-width);width:var(--wp--lightbox-image-width)}.wp-lightbox-overlay .wp-block-image figcaption{display:none}.wp-lightbox-overlay button{background:none;border:none}.wp-lightbox-overlay .scrim{background-color:#fff;height:100%;opacity:.9;position:absolute;width:100%;z-index:2000000}.wp-lightbox-overlay.active{visibility:visible}@media not (prefers-reduced-motion){.wp-lightbox-overlay.active{animation:turn-on-visibility .25s both}.wp-lightbox-overlay.active img{animation:turn-on-visibility .35s both}.wp-lightbox-overlay.show-closing-animation:not(.active){animation:turn-off-visibility .35s both}.wp-lightbox-overlay.show-closing-animation:not(.active) img{animation:turn-off-visibility .25s both}.wp-lightbox-overlay.zoom.active{animation:none;opacity:1;visibility:visible}.wp-lightbox-overlay.zoom.active .lightbox-image-container{animation:lightbox-zoom-in .4s}.wp-lightbox-overlay.zoom.active .lightbox-image-container img{animation:none}.wp-lightbox-overlay.zoom.active .scrim{animation:turn-on-visibility .4s forwards}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active){animation:none}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .lightbox-image-container{animation:lightbox-zoom-out .4s}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .lightbox-image-container img{animation:none}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .scrim{animation:turn-off-visibility .4s forwards}}@keyframes show-content-image{0%{visibility:hidden}99%{visibility:hidden}to{visibility:visible}}@keyframes turn-on-visibility{0%{opacity:0}to{opacity:1}}@keyframes turn-off-visibility{0%{opacity:1;visibility:visible}99%{opacity:0;visibility:visible}to{opacity:0;visibility:hidden}}@keyframes lightbox-zoom-in{0%{transform:translate(calc((-100vw + var(--wp--lightbox-scrollbar-width))/2 + var(--wp--lightbox-initial-left-position)),calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale))}to{transform:translate(-50%,-50%) scale(1)}}@keyframes lightbox-zoom-out{0%{transform:translate(-50%,-50%) scale(1);visibility:visible}99%{visibility:visible}to{transform:translate(calc((-100vw + var(--wp--lightbox-scrollbar-width))/2 + var(--wp--lightbox-initial-left-position)),calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale));visibility:hidden}}\n/*# sourceURL=https://github.blog/wp-includes/blocks/image/style.min.css */\n</style>\n<style id=\"wp-block-buttons-inline-css\">\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://github.blog/wp-includes/blocks/buttons/style.min.css */\n</style>\n<style id=\"wp-block-paragraph-inline-css\">\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://github.blog/wp-includes/blocks/paragraph/style.min.css */\n</style>\n<style id=\"github-typography-presets-inline-css\">\n.is-typography-preset-h1{font-size:var(--base-size-36, 36px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:0;}.is-typography-preset-h2{font-size:var(--base-size-32, 32px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:40px;}.is-typography-preset-h3{font-size:var(--base-size-28, 28px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h4{font-size:var(--base-size-24, 24px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h5{font-size:var(--base-size-20, 20px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h6{font-size:var(--base-size-18, 18px);font-weight:700;line-height:1.25;margin-block-end:0;margin-block-start:20px;}.is-typography-preset-display{font:var(--text-display-shorthand);}.is-typography-preset-title-large{font:var(--text-title-shorthand-large);}.is-typography-preset-title-medium{font:var(--text-title-shorthand-medium);}.is-typography-preset-title-small{font:var(--text-title-shorthand-small);}.is-typography-preset-subtitle{font:var(--text-subtitle-shorthand);}.is-typography-preset-body-large{font:var(--text-body-shorthand-large);}.is-typography-preset-body-medium{font:var(--text-body-shorthand-medium);}.is-typography-preset-body-small{font:var(--text-body-shorthand-small);}\n/*# sourceURL=github-typography-presets-inline-css */\n</style>\n<style id=\"global-styles-inline-css\">\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--spacing--20: var(--base-size-20, 1.25rem);--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: var(--base-size-40, 2.5rem);--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: var(--base-size-80, 5rem);--wp--preset--spacing--4: var(--base-size-4, 0.25rem);--wp--preset--spacing--6: var(--base-size-6, 0.375rem);--wp--preset--spacing--8: var(--base-size-8, 0.5rem);--wp--preset--spacing--2: var(--base-size-2, 0.125rem);--wp--preset--spacing--12: var(--base-size-12, 0.75rem);--wp--preset--spacing--16: var(--base-size-16, 1rem);--wp--preset--spacing--24: var(--base-size-24, 1.5rem);--wp--preset--spacing--28: var(--base-size-28, 1.75rem);--wp--preset--spacing--32: var(--base-size-32, 2rem);--wp--preset--spacing--36: var(--base-size-36, 2.25rem);--wp--preset--spacing--44: var(--base-size-44, 2.75rem);--wp--preset--spacing--48: var(--base-size-48, 3rem);--wp--preset--spacing--64: var(--base-size-64, 4rem);--wp--preset--spacing--96: var(--base-size-96, 6rem);--wp--preset--spacing--112: var(--base-size-112, 7rem);--wp--preset--spacing--128: var(--base-size-128, 8rem);--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--typography--presets--0--name: Like h1;--wp--custom--typography--presets--0--slug: h1;--wp--custom--typography--presets--0--font-size: var(--base-size-36, 36px);--wp--custom--typography--presets--0--font-weight: 700;--wp--custom--typography--presets--0--line-height: 1.25;--wp--custom--typography--presets--0--margin-block-end: 20px;--wp--custom--typography--presets--0--margin-block-start: 0;--wp--custom--typography--presets--1--name: Like h2;--wp--custom--typography--presets--1--slug: h2;--wp--custom--typography--presets--1--font-size: var(--base-size-32, 32px);--wp--custom--typography--presets--1--font-weight: 700;--wp--custom--typography--presets--1--line-height: 1.25;--wp--custom--typography--presets--1--margin-block-end: 20px;--wp--custom--typography--presets--1--margin-block-start: 40px;--wp--custom--typography--presets--2--name: Like h3;--wp--custom--typography--presets--2--slug: h3;--wp--custom--typography--presets--2--font-size: var(--base-size-28, 28px);--wp--custom--typography--presets--2--font-weight: 700;--wp--custom--typography--presets--2--line-height: 1.25;--wp--custom--typography--presets--2--margin-block-end: 12px;--wp--custom--typography--presets--2--margin-block-start: 20px;--wp--custom--typography--presets--3--name: Like h4;--wp--custom--typography--presets--3--slug: h4;--wp--custom--typography--presets--3--font-size: var(--base-size-24, 24px);--wp--custom--typography--presets--3--font-weight: 700;--wp--custom--typography--presets--3--line-height: 1.25;--wp--custom--typography--presets--3--margin-block-end: 12px;--wp--custom--typography--presets--3--margin-block-start: 20px;--wp--custom--typography--presets--4--name: Like h5;--wp--custom--typography--presets--4--slug: h5;--wp--custom--typography--presets--4--font-size: var(--base-size-20, 20px);--wp--custom--typography--presets--4--font-weight: 700;--wp--custom--typography--presets--4--line-height: 1.25;--wp--custom--typography--presets--4--margin-block-end: 12px;--wp--custom--typography--presets--4--margin-block-start: 20px;--wp--custom--typography--presets--5--name: Like h6;--wp--custom--typography--presets--5--slug: h6;--wp--custom--typography--presets--5--font-size: var(--base-size-18, 18px);--wp--custom--typography--presets--5--font-weight: 700;--wp--custom--typography--presets--5--line-height: 1.25;--wp--custom--typography--presets--5--margin-block-end: 0;--wp--custom--typography--presets--5--margin-block-start: 20px;--wp--custom--typography--presets--6--name: Display;--wp--custom--typography--presets--6--slug: display;--wp--custom--typography--presets--6--font: var(--text-display-shorthand);--wp--custom--typography--presets--7--name: Title Large;--wp--custom--typography--presets--7--slug: title-large;--wp--custom--typography--presets--7--font: var(--text-title-shorthand-large);--wp--custom--typography--presets--8--name: Title Medium;--wp--custom--typography--presets--8--slug: title-medium;--wp--custom--typography--presets--8--font: var(--text-title-shorthand-medium);--wp--custom--typography--presets--9--name: Title Small;--wp--custom--typography--presets--9--slug: title-small;--wp--custom--typography--presets--9--font: var(--text-title-shorthand-small);--wp--custom--typography--presets--10--name: Subtitle;--wp--custom--typography--presets--10--slug: subtitle;--wp--custom--typography--presets--10--font: var(--text-subtitle-shorthand);--wp--custom--typography--presets--11--name: Body Large;--wp--custom--typography--presets--11--slug: body-large;--wp--custom--typography--presets--11--font: var(--text-body-shorthand-large);--wp--custom--typography--presets--12--name: Body Medium;--wp--custom--typography--presets--12--slug: body-medium;--wp--custom--typography--presets--12--font: var(--text-body-shorthand-medium);--wp--custom--typography--presets--13--name: Body Small;--wp--custom--typography--presets--13--slug: body-small;--wp--custom--typography--presets--13--font: var(--text-body-shorthand-small);}:root { --wp--style--global--content-size: calc(1280px - var(--p-responsive-blog) * 2);--wp--style--global--wide-size: calc(1280px - var(--p-responsive-blog) * 2); }:where(body) { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) { padding-right: 0; padding-left: 0; }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) > .alignfull { margin-left: 0; margin-right: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{font-family: var(--brand-fontStack-sansSerif, sans-serif);--wp--style--root--padding-top: 0px;--wp--style--root--padding-right: var(--p-responsive-blog);--wp--style--root--padding-bottom: 0px;--wp--style--root--padding-left: var(--p-responsive-blog);}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n<link href=\"/rss-offline/assets/c05e51dd412952e4/59be3494fbcb9f18.css\" id=\"all-css-8\" media=\"all\" rel=\"stylesheet\" type=\"text/css\"/>\n<link href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css?ver=11.4.0\" id=\"highlightjs-css-css\" media=\"all\" rel=\"stylesheet\"/>\n<link href=\"https://github.blog/wp-json/\" rel=\"https://api.w.org/\"/><link href=\"https://github.blog/wp-json/wp/v2/posts/96339\" rel=\"alternate\" title=\"JSON\" type=\"application/json\"/><link href=\"https://github.blog/xmlrpc.php?rsd\" rel=\"EditURI\" title=\"RSD\" type=\"application/rsd+xml\"/>\n<meta content=\"WordPress 6.9.4\" name=\"generator\"/>\n<link href=\"https://wp.me/pamS32-p3R\" rel=\"shortlink\"/>\n<!-- Stream WordPress user activity plugin v4.1.2 -->\n<style>img#wpstats{display:none}</style>\n<link href=\"https://github.blog/llms.txt\" rel=\"llms-sitemap\"/>\n<meta content=\"https://collector.githubapp.com/github-blog/collect\" name=\"ha-url\"/><link as=\"font\" crossorigin=\"anonymous\" href=\"https://github.blog/wp-content/themes/github-2021/dist/fonts/mona-sans.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"anonymous\" href=\"https://github.blog/wp-content/themes/github-2021/dist/fonts/MonaspaceNeon-Regular.woff2\" rel=\"preload\" type=\"font/woff2\"/><link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=32%2C32\" rel=\"icon\" sizes=\"32x32\"/>\n<link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=192%2C192\" rel=\"icon\" sizes=\"192x192\"/>\n<link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=180%2C180\" rel=\"apple-touch-icon\"/>\n<meta content=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=270%2C270\" name=\"msapplication-TileImage\"/>\n<style id=\"core-block-supports-inline-css\">\n.wp-container-core-buttons-is-layout-a89b3969{justify-content:center;}\n/*# sourceURL=core-block-supports-inline-css */\n</style>\n<style id=\"wp-style-engine-github-typography-presets-inline-css\">\n.is-typography-preset-h1{font-size:var(--base-size-36, 36px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:0;}.is-typography-preset-h2{font-size:var(--base-size-32, 32px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:40px;}.is-typography-preset-h3{font-size:var(--base-size-28, 28px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h4{font-size:var(--base-size-24, 24px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h5{font-size:var(--base-size-20, 20px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h6{font-size:var(--base-size-18, 18px);font-weight:700;line-height:1.25;margin-block-end:0;margin-block-start:20px;}.is-typography-preset-display{font:var(--text-display-shorthand);}.is-typography-preset-title-large{font:var(--text-title-shorthand-large);}.is-typography-preset-title-medium{font:var(--text-title-shorthand-medium);}.is-typography-preset-title-small{font:var(--text-title-shorthand-small);}.is-typography-preset-subtitle{font:var(--text-subtitle-shorthand);}.is-typography-preset-body-large{font:var(--text-body-shorthand-large);}.is-typography-preset-body-medium{font:var(--text-body-shorthand-medium);}.is-typography-preset-body-small{font:var(--text-body-shorthand-small);}\n/*# sourceURL=wp-style-engine-github-typography-presets-inline-css */\n</style>\n\n[Showing lines 1-117 of 1003 (50.0KB limit). Use offset=118 to continue.]"}],"details":{"truncation":{"content":"<!doctype html>\n<!DOCTYPE html>\n\n<html class=\"mt-0\" lang=\"en-US\" style=\"--navigation-offset: 0px; --header-offset: 75px;\"><head><base href=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\"/>\n<meta charset=\"utf-8\"/>\n<meta content=\"width=device-width, initial-scale=1\" name=\"viewport\"/>\n<link href=\"https://gmpg.org/xfn/11\" rel=\"profile\"/>\n<link href=\"https://github.githubassets.com/favicon.ico\" rel=\"icon\" type=\"image/x-icon\"/>\n<meta content=\"index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1\" name=\"robots\"/>\n<!-- This site is optimized with the Yoast SEO Premium plugin v27.5 (Yoast SEO v27.5) - https://yoast.com/product/yoast-seo-premium-wordpress/ -->\n<title>Still a developer. Just outside. Our latest GitHub Shop collection is here. - The GitHub Blog</title>\n<meta content=\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\" name=\"description\"/>\n<link href=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\" rel=\"canonical\"/>\n<meta content=\"en_US\" property=\"og:locale\"/>\n<meta content=\"article\" property=\"og:type\"/>\n<meta content=\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\" property=\"og:title\"/>\n<meta content=\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\" property=\"og:description\"/>\n<meta content=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\" property=\"og:url\"/>\n<meta content=\"The GitHub Blog\" property=\"og:site_name\"/>\n<meta content=\"2026-05-28T18:18:43+00:00\" property=\"article:published_time\"/>\n<meta content=\"2026-05-28T18:18:46+00:00\" property=\"article:modified_time\"/>\n<meta content=\"https://github.blog/wp-content/uploads/2026/05/Blog-Header.png\" property=\"og:image\"/>\n<meta content=\"2286\" property=\"og:image:width\"/>\n<meta content=\"1210\" property=\"og:image:height\"/>\n<meta content=\"image/png\" property=\"og:image:type\"/>\n<meta content=\"Lavinia Sfetcu\" name=\"author\"/>\n<meta content=\"summary_large_image\" name=\"twitter:card\"/>\n<meta content=\"Written by\" name=\"twitter:label1\"/>\n<meta content=\"Lavinia Sfetcu\" name=\"twitter:data1\"/>\n<meta content=\"Est. reading time\" name=\"twitter:label2\"/>\n<meta content=\"3 minutes\" name=\"twitter:data2\"/>\n<script type=\"application/ld+json\">{\"@context\":\"https:\\/\\/schema.org\",\"headline\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\",\"author\":{\"@type\":\"Person\",\"name\":\"Natalie Guevara\"},\"datePublished\":\"2026-05-28T11:18:43-07:00\",\"abstract\":\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\",\"mainEntityOfPage\":{\"@type\":\"WebPage\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"},\"image\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?resize=150%2C150\",\"@type\":\"TechArticle\"}</script>\n<script type=\"application/ld+json\">{\"@context\":\"https:\\/\\/schema.org\",\"headline\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\",\"author\":{\"@type\":\"Person\",\"name\":\"Natalie Guevara\"},\"datePublished\":\"2026-05-28T11:18:43-07:00\",\"abstract\":\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\",\"mainEntityOfPage\":{\"@type\":\"WebPage\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"},\"image\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?resize=150%2C150\",\"@type\":\"BlogPosting\"}</script>\n<script class=\"yoast-schema-graph\" type=\"application/ld+json\">{\"@context\":\"https:\\/\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#article\",\"isPartOf\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"},\"author\":[{\"@id\":\"https:\\/\\/github.blog\\/#\\/schema\\/person\\/5e0ebc47a163812542fb84009a1eadac\"}],\"headline\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\",\"datePublished\":\"2026-05-28T18:18:43+00:00\",\"dateModified\":\"2026-05-28T18:18:46+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"},\"wordCount\":423,\"image\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?fit=2286%2C1210\",\"keywords\":[\"GitHub Shop\"],\"articleSection\":[\"Company news\",\"News &amp; insights\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\",\"url\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\",\"name\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here. - The GitHub Blog\",\"isPartOf\":{\"@id\":\"https:\\/\\/github.blog\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?fit=2286%2C1210\",\"datePublished\":\"2026-05-28T18:18:43+00:00\",\"dateModified\":\"2026-05-28T18:18:46+00:00\",\"author\":{\"@id\":\"https:\\/\\/github.blog\\/#\\/schema\\/person\\/37273876e05b418f9e59da3267030711\"},\"description\":\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\",\"breadcrumb\":{\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#primaryimage\",\"url\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?fit=2286%2C1210\",\"contentUrl\":\"https:\\/\\/github.blog\\/wp-content\\/uploads\\/2026\\/05\\/Blog-Header.png?fit=2286%2C1210\",\"width\":2286,\"height\":1210,\"caption\":\"Items from the new ESC collection at the GitHub Shop are featured in front of a colorful background. Items include a hoodie coozie, a tee shirt, and a Cabana set of matching shirt and shorts.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/still-a-developer-just-outside-our-latest-github-shop-collection-is-here\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\/\\/github.blog\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"News &amp; insights\",\"item\":\"https:\\/\\/github.blog\\/news-insights\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Company news\",\"item\":\"https:\\/\\/github.blog\\/news-insights\\/company-news\\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\/\\/github.blog\\/#website\",\"url\":\"https:\\/\\/github.blog\\/\",\"name\":\"The GitHub Blog\",\"description\":\"Updates, ideas, and inspiration from GitHub to help developers build and design software.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\/\\/github.blog\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\/\\/github.blog\\/#\\/schema\\/person\\/5e0ebc47a163812542fb84009a1eadac\",\"name\":\"Lavinia Sfetcu\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\/\\/secure.gravatar.com\\/avatar\\/294ad6be270da24048c247d3ef65839df645a862e46ff6547dd2ae605f4aa437?s=96&d=mm&r=ge545d325921a67df965e10ee1f12a9d1\",\"url\":\"https:\\/\\/secure.gravatar.com\\/avatar\\/294ad6be270da24048c247d3ef65839df645a862e46ff6547dd2ae605f4aa437?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\/\\/secure.gravatar.com\\/avatar\\/294ad6be270da24048c247d3ef65839df645a862e46ff6547dd2ae605f4aa437?s=96&d=mm&r=g\",\"caption\":\"Lavinia Sfetcu\"},\"url\":\"https:\\/\\/github.blog\\/author\\/laviniasf\\/\"}]}</script>\n<!-- / Yoast SEO Premium plugin. -->\n<link href=\"//ghcc.githubassets.com\" rel=\"dns-prefetch\"/>\n<link href=\"//js.monitor.azure.com\" rel=\"dns-prefetch\"/>\n<link href=\"//cdnjs.cloudflare.com\" rel=\"dns-prefetch\"/>\n<link href=\"//analytics.githubassets.com\" rel=\"dns-prefetch\"/>\n<link href=\"//stats.wp.com\" rel=\"dns-prefetch\"/>\n<link href=\"//v0.wordpress.com\" rel=\"dns-prefetch\"/>\n<link href=\"https://github.blog/feed/\" rel=\"alternate\" title=\"The GitHub Blog » Feed\" type=\"application/rss+xml\"/>\n<link href=\"https://github.blog/comments/feed/\" rel=\"alternate\" title=\"The GitHub Blog » Comments Feed\" type=\"application/rss+xml\"/>\n<link href=\"https://github.blog/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fgithub.blog%2Fnews-insights%2Fcompany-news%2Fstill-a-developer-just-outside-our-latest-github-shop-collection-is-here%2F\" rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\"/>\n<link href=\"https://github.blog/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fgithub.blog%2Fnews-insights%2Fcompany-news%2Fstill-a-developer-just-outside-our-latest-github-shop-collection-is-here%2F&amp;format=xml\" rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\"/>\n<style id=\"wp-img-auto-sizes-contain-inline-css\">\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id=\"wp-emoji-styles-inline-css\">\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id=\"wp-block-library-inline-css\">\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}.has-text-align-center{text-align:center}.has-text-align-left{text-align:left}.has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;word-wrap:normal!important}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style>\n<style id=\"wp-block-button-inline-css\">\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://github.blog/wp-includes/blocks/button/style.min.css */\n</style>\n<style id=\"wp-block-heading-inline-css\">\nh1:where(.wp-block-heading).has-background,h2:where(.wp-block-heading).has-background,h3:where(.wp-block-heading).has-background,h4:where(.wp-block-heading).has-background,h5:where(.wp-block-heading).has-background,h6:where(.wp-block-heading).has-background{padding:1.25em 2.375em}h1.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h1.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h2.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h2.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h3.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h3.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h4.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h4.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h5.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h5.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h6.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h6.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]){rotate:180deg}\n/*# sourceURL=https://github.blog/wp-includes/blocks/heading/style.min.css */\n</style>\n<style id=\"wp-block-image-inline-css\">\n.wp-block-image>a,.wp-block-image>figure>a{display:inline-block}.wp-block-image img{box-sizing:border-box;height:auto;max-width:100%;vertical-align:bottom}@media not (prefers-reduced-motion){.wp-block-image img.hide{visibility:hidden}.wp-block-image img.show{animation:show-content-image .4s}}.wp-block-image[style*=border-radius] img,.wp-block-image[style*=border-radius]>a{border-radius:inherit}.wp-block-image.has-custom-border img{box-sizing:border-box}.wp-block-image.aligncenter{text-align:center}.wp-block-image.alignfull>a,.wp-block-image.alignwide>a{width:100%}.wp-block-image.alignfull img,.wp-block-image.alignwide img{height:auto;width:100%}.wp-block-image .aligncenter,.wp-block-image .alignleft,.wp-block-image .alignright,.wp-block-image.aligncenter,.wp-block-image.alignleft,.wp-block-image.alignright{display:table}.wp-block-image .aligncenter>figcaption,.wp-block-image .alignleft>figcaption,.wp-block-image .alignright>figcaption,.wp-block-image.aligncenter>figcaption,.wp-block-image.alignleft>figcaption,.wp-block-image.alignright>figcaption{caption-side:bottom;display:table-caption}.wp-block-image .alignleft{float:left;margin:.5em 1em .5em 0}.wp-block-image .alignright{float:right;margin:.5em 0 .5em 1em}.wp-block-image .aligncenter{margin-left:auto;margin-right:auto}.wp-block-image :where(figcaption){margin-bottom:1em;margin-top:.5em}.wp-block-image.is-style-circle-mask img{border-radius:9999px}@supports ((-webkit-mask-image:none) or (mask-image:none)) or (-webkit-mask-image:none){.wp-block-image.is-style-circle-mask img{border-radius:0;-webkit-mask-image:url('data:image/svg+xml;utf8,<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>');mask-mode:alpha;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}}:root :where(.wp-block-image.is-style-rounded img,.wp-block-image .is-style-rounded img){border-radius:9999px}.wp-block-image figure{margin:0}.wp-lightbox-container{display:flex;flex-direction:column;position:relative}.wp-lightbox-container img{cursor:zoom-in}.wp-lightbox-container img:hover+button{opacity:1}.wp-lightbox-container button{align-items:center;backdrop-filter:blur(16px) saturate(180%);background-color:#5a5a5a40;border:none;border-radius:4px;cursor:zoom-in;display:flex;height:20px;justify-content:center;opacity:0;padding:0;position:absolute;right:16px;text-align:center;top:16px;width:20px;z-index:100}@media not (prefers-reduced-motion){.wp-lightbox-container button{transition:opacity .2s ease}}.wp-lightbox-container button:focus-visible{outline:3px auto #5a5a5a40;outline:3px auto -webkit-focus-ring-color;outline-offset:3px}.wp-lightbox-container button:hover{cursor:pointer;opacity:1}.wp-lightbox-container button:focus{opacity:1}.wp-lightbox-container button:focus,.wp-lightbox-container button:hover,.wp-lightbox-container button:not(:hover):not(:active):not(.has-background){background-color:#5a5a5a40;border:none}.wp-lightbox-overlay{box-sizing:border-box;cursor:zoom-out;height:100vh;left:0;overflow:hidden;position:fixed;top:0;visibility:hidden;width:100%;z-index:100000}.wp-lightbox-overlay .close-button{align-items:center;cursor:pointer;display:flex;justify-content:center;min-height:40px;min-width:40px;padding:0;position:absolute;right:calc(env(safe-area-inset-right) + 16px);top:calc(env(safe-area-inset-top) + 16px);z-index:5000000}.wp-lightbox-overlay .close-button:focus,.wp-lightbox-overlay .close-button:hover,.wp-lightbox-overlay .close-button:not(:hover):not(:active):not(.has-background){background:none;border:none}.wp-lightbox-overlay .lightbox-image-container{height:var(--wp--lightbox-container-height);left:50%;overflow:hidden;position:absolute;top:50%;transform:translate(-50%,-50%);transform-origin:top left;width:var(--wp--lightbox-container-width);z-index:9999999999}.wp-lightbox-overlay .wp-block-image{align-items:center;box-sizing:border-box;display:flex;height:100%;justify-content:center;margin:0;position:relative;transform-origin:0 0;width:100%;z-index:3000000}.wp-lightbox-overlay .wp-block-image img{height:var(--wp--lightbox-image-height);min-height:var(--wp--lightbox-image-height);min-width:var(--wp--lightbox-image-width);width:var(--wp--lightbox-image-width)}.wp-lightbox-overlay .wp-block-image figcaption{display:none}.wp-lightbox-overlay button{background:none;border:none}.wp-lightbox-overlay .scrim{background-color:#fff;height:100%;opacity:.9;position:absolute;width:100%;z-index:2000000}.wp-lightbox-overlay.active{visibility:visible}@media not (prefers-reduced-motion){.wp-lightbox-overlay.active{animation:turn-on-visibility .25s both}.wp-lightbox-overlay.active img{animation:turn-on-visibility .35s both}.wp-lightbox-overlay.show-closing-animation:not(.active){animation:turn-off-visibility .35s both}.wp-lightbox-overlay.show-closing-animation:not(.active) img{animation:turn-off-visibility .25s both}.wp-lightbox-overlay.zoom.active{animation:none;opacity:1;visibility:visible}.wp-lightbox-overlay.zoom.active .lightbox-image-container{animation:lightbox-zoom-in .4s}.wp-lightbox-overlay.zoom.active .lightbox-image-container img{animation:none}.wp-lightbox-overlay.zoom.active .scrim{animation:turn-on-visibility .4s forwards}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active){animation:none}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .lightbox-image-container{animation:lightbox-zoom-out .4s}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .lightbox-image-container img{animation:none}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .scrim{animation:turn-off-visibility .4s forwards}}@keyframes show-content-image{0%{visibility:hidden}99%{visibility:hidden}to{visibility:visible}}@keyframes turn-on-visibility{0%{opacity:0}to{opacity:1}}@keyframes turn-off-visibility{0%{opacity:1;visibility:visible}99%{opacity:0;visibility:visible}to{opacity:0;visibility:hidden}}@keyframes lightbox-zoom-in{0%{transform:translate(calc((-100vw + var(--wp--lightbox-scrollbar-width))/2 + var(--wp--lightbox-initial-left-position)),calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale))}to{transform:translate(-50%,-50%) scale(1)}}@keyframes lightbox-zoom-out{0%{transform:translate(-50%,-50%) scale(1);visibility:visible}99%{visibility:visible}to{transform:translate(calc((-100vw + var(--wp--lightbox-scrollbar-width))/2 + var(--wp--lightbox-initial-left-position)),calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale));visibility:hidden}}\n/*# sourceURL=https://github.blog/wp-includes/blocks/image/style.min.css */\n</style>\n<style id=\"wp-block-buttons-inline-css\">\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://github.blog/wp-includes/blocks/buttons/style.min.css */\n</style>\n<style id=\"wp-block-paragraph-inline-css\">\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://github.blog/wp-includes/blocks/paragraph/style.min.css */\n</style>\n<style id=\"github-typography-presets-inline-css\">\n.is-typography-preset-h1{font-size:var(--base-size-36, 36px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:0;}.is-typography-preset-h2{font-size:var(--base-size-32, 32px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:40px;}.is-typography-preset-h3{font-size:var(--base-size-28, 28px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h4{font-size:var(--base-size-24, 24px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h5{font-size:var(--base-size-20, 20px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h6{font-size:var(--base-size-18, 18px);font-weight:700;line-height:1.25;margin-block-end:0;margin-block-start:20px;}.is-typography-preset-display{font:var(--text-display-shorthand);}.is-typography-preset-title-large{font:var(--text-title-shorthand-large);}.is-typography-preset-title-medium{font:var(--text-title-shorthand-medium);}.is-typography-preset-title-small{font:var(--text-title-shorthand-small);}.is-typography-preset-subtitle{font:var(--text-subtitle-shorthand);}.is-typography-preset-body-large{font:var(--text-body-shorthand-large);}.is-typography-preset-body-medium{font:var(--text-body-shorthand-medium);}.is-typography-preset-body-small{font:var(--text-body-shorthand-small);}\n/*# sourceURL=github-typography-presets-inline-css */\n</style>\n<style id=\"global-styles-inline-css\">\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--spacing--20: var(--base-size-20, 1.25rem);--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: var(--base-size-40, 2.5rem);--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: var(--base-size-80, 5rem);--wp--preset--spacing--4: var(--base-size-4, 0.25rem);--wp--preset--spacing--6: var(--base-size-6, 0.375rem);--wp--preset--spacing--8: var(--base-size-8, 0.5rem);--wp--preset--spacing--2: var(--base-size-2, 0.125rem);--wp--preset--spacing--12: var(--base-size-12, 0.75rem);--wp--preset--spacing--16: var(--base-size-16, 1rem);--wp--preset--spacing--24: var(--base-size-24, 1.5rem);--wp--preset--spacing--28: var(--base-size-28, 1.75rem);--wp--preset--spacing--32: var(--base-size-32, 2rem);--wp--preset--spacing--36: var(--base-size-36, 2.25rem);--wp--preset--spacing--44: var(--base-size-44, 2.75rem);--wp--preset--spacing--48: var(--base-size-48, 3rem);--wp--preset--spacing--64: var(--base-size-64, 4rem);--wp--preset--spacing--96: var(--base-size-96, 6rem);--wp--preset--spacing--112: var(--base-size-112, 7rem);--wp--preset--spacing--128: var(--base-size-128, 8rem);--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--typography--presets--0--name: Like h1;--wp--custom--typography--presets--0--slug: h1;--wp--custom--typography--presets--0--font-size: var(--base-size-36, 36px);--wp--custom--typography--presets--0--font-weight: 700;--wp--custom--typography--presets--0--line-height: 1.25;--wp--custom--typography--presets--0--margin-block-end: 20px;--wp--custom--typography--presets--0--margin-block-start: 0;--wp--custom--typography--presets--1--name: Like h2;--wp--custom--typography--presets--1--slug: h2;--wp--custom--typography--presets--1--font-size: var(--base-size-32, 32px);--wp--custom--typography--presets--1--font-weight: 700;--wp--custom--typography--presets--1--line-height: 1.25;--wp--custom--typography--presets--1--margin-block-end: 20px;--wp--custom--typography--presets--1--margin-block-start: 40px;--wp--custom--typography--presets--2--name: Like h3;--wp--custom--typography--presets--2--slug: h3;--wp--custom--typography--presets--2--font-size: var(--base-size-28, 28px);--wp--custom--typography--presets--2--font-weight: 700;--wp--custom--typography--presets--2--line-height: 1.25;--wp--custom--typography--presets--2--margin-block-end: 12px;--wp--custom--typography--presets--2--margin-block-start: 20px;--wp--custom--typography--presets--3--name: Like h4;--wp--custom--typography--presets--3--slug: h4;--wp--custom--typography--presets--3--font-size: var(--base-size-24, 24px);--wp--custom--typography--presets--3--font-weight: 700;--wp--custom--typography--presets--3--line-height: 1.25;--wp--custom--typography--presets--3--margin-block-end: 12px;--wp--custom--typography--presets--3--margin-block-start: 20px;--wp--custom--typography--presets--4--name: Like h5;--wp--custom--typography--presets--4--slug: h5;--wp--custom--typography--presets--4--font-size: var(--base-size-20, 20px);--wp--custom--typography--presets--4--font-weight: 700;--wp--custom--typography--presets--4--line-height: 1.25;--wp--custom--typography--presets--4--margin-block-end: 12px;--wp--custom--typography--presets--4--margin-block-start: 20px;--wp--custom--typography--presets--5--name: Like h6;--wp--custom--typography--presets--5--slug: h6;--wp--custom--typography--presets--5--font-size: var(--base-size-18, 18px);--wp--custom--typography--presets--5--font-weight: 700;--wp--custom--typography--presets--5--line-height: 1.25;--wp--custom--typography--presets--5--margin-block-end: 0;--wp--custom--typography--presets--5--margin-block-start: 20px;--wp--custom--typography--presets--6--name: Display;--wp--custom--typography--presets--6--slug: display;--wp--custom--typography--presets--6--font: var(--text-display-shorthand);--wp--custom--typography--presets--7--name: Title Large;--wp--custom--typography--presets--7--slug: title-large;--wp--custom--typography--presets--7--font: var(--text-title-shorthand-large);--wp--custom--typography--presets--8--name: Title Medium;--wp--custom--typography--presets--8--slug: title-medium;--wp--custom--typography--presets--8--font: var(--text-title-shorthand-medium);--wp--custom--typography--presets--9--name: Title Small;--wp--custom--typography--presets--9--slug: title-small;--wp--custom--typography--presets--9--font: var(--text-title-shorthand-small);--wp--custom--typography--presets--10--name: Subtitle;--wp--custom--typography--presets--10--slug: subtitle;--wp--custom--typography--presets--10--font: var(--text-subtitle-shorthand);--wp--custom--typography--presets--11--name: Body Large;--wp--custom--typography--presets--11--slug: body-large;--wp--custom--typography--presets--11--font: var(--text-body-shorthand-large);--wp--custom--typography--presets--12--name: Body Medium;--wp--custom--typography--presets--12--slug: body-medium;--wp--custom--typography--presets--12--font: var(--text-body-shorthand-medium);--wp--custom--typography--presets--13--name: Body Small;--wp--custom--typography--presets--13--slug: body-small;--wp--custom--typography--presets--13--font: var(--text-body-shorthand-small);}:root { --wp--style--global--content-size: calc(1280px - var(--p-responsive-blog) * 2);--wp--style--global--wide-size: calc(1280px - var(--p-responsive-blog) * 2); }:where(body) { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) { padding-right: 0; padding-left: 0; }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) > .alignfull { margin-left: 0; margin-right: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{font-family: var(--brand-fontStack-sansSerif, sans-serif);--wp--style--root--padding-top: 0px;--wp--style--root--padding-right: var(--p-responsive-blog);--wp--style--root--padding-bottom: 0px;--wp--style--root--padding-left: var(--p-responsive-blog);}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n<link href=\"/rss-offline/assets/c05e51dd412952e4/59be3494fbcb9f18.css\" id=\"all-css-8\" media=\"all\" rel=\"stylesheet\" type=\"text/css\"/>\n<link href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css?ver=11.4.0\" id=\"highlightjs-css-css\" media=\"all\" rel=\"stylesheet\"/>\n<link href=\"https://github.blog/wp-json/\" rel=\"https://api.w.org/\"/><link href=\"https://github.blog/wp-json/wp/v2/posts/96339\" rel=\"alternate\" title=\"JSON\" type=\"application/json\"/><link href=\"https://github.blog/xmlrpc.php?rsd\" rel=\"EditURI\" title=\"RSD\" type=\"application/rsd+xml\"/>\n<meta content=\"WordPress 6.9.4\" name=\"generator\"/>\n<link href=\"https://wp.me/pamS32-p3R\" rel=\"shortlink\"/>\n<!-- Stream WordPress user activity plugin v4.1.2 -->\n<style>img#wpstats{display:none}</style>\n<link href=\"https://github.blog/llms.txt\" rel=\"llms-sitemap\"/>\n<meta content=\"https://collector.githubapp.com/github-blog/collect\" name=\"ha-url\"/><link as=\"font\" crossorigin=\"anonymous\" href=\"https://github.blog/wp-content/themes/github-2021/dist/fonts/mona-sans.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"anonymous\" href=\"https://github.blog/wp-content/themes/github-2021/dist/fonts/MonaspaceNeon-Regular.woff2\" rel=\"preload\" type=\"font/woff2\"/><link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=32%2C32\" rel=\"icon\" sizes=\"32x32\"/>\n<link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=192%2C192\" rel=\"icon\" sizes=\"192x192\"/>\n<link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=180%2C180\" rel=\"apple-touch-icon\"/>\n<meta content=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=270%2C270\" name=\"msapplication-TileImage\"/>\n<style id=\"core-block-supports-inline-css\">\n.wp-container-core-buttons-is-layout-a89b3969{justify-content:center;}\n/*# sourceURL=core-block-supports-inline-css */\n</style>\n<style id=\"wp-style-engine-github-typography-presets-inline-css\">\n.is-typography-preset-h1{font-size:var(--base-size-36, 36px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:0;}.is-typography-preset-h2{font-size:var(--base-size-32, 32px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:40px;}.is-typography-preset-h3{font-size:var(--base-size-28, 28px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h4{font-size:var(--base-size-24, 24px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h5{font-size:var(--base-size-20, 20px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h6{font-size:var(--base-size-18, 18px);font-weight:700;line-height:1.25;margin-block-end:0;margin-block-start:20px;}.is-typography-preset-display{font:var(--text-display-shorthand);}.is-typography-preset-title-large{font:var(--text-title-shorthand-large);}.is-typography-preset-title-medium{font:var(--text-title-shorthand-medium);}.is-typography-preset-title-small{font:var(--text-title-shorthand-small);}.is-typography-preset-subtitle{font:var(--text-subtitle-shorthand);}.is-typography-preset-body-large{font:var(--text-body-shorthand-large);}.is-typography-preset-body-medium{font:var(--text-body-shorthand-medium);}.is-typography-preset-body-small{font:var(--text-body-shorthand-small);}\n/*# sourceURL=wp-style-engine-github-typography-presets-inline-css */\n</style>","truncated":true,"truncatedBy":"bytes","totalLines":220,"totalBytes":70047,"outputLines":117,"outputBytes":49368,"lastLinePartial":false,"firstLineExceedsLimit":false,"maxLines":2000,"maxBytes":51200}},"isError":false,"timestamp":1780219531843}}
{"type":"message","id":"048c4bbe","parentId":"4abf6e13","timestamp":"2026-05-31T09:25:31.844Z","message":{"role":"toolResult","toolCallId":"call_5Vx1dcNiLFATM3JbQmcGJMrD|fc_0f029265420fe376016a1bfe8bab288191b0882eab7a7a122e","toolName":"read","content":[{"type":"text","text":"<!DOCTYPE html>\n\n<!DOCTYPE html>\n\n<html class=\"mt-0\" lang=\"en-US\" style=\"--navigation-offset: 0px; --header-offset: 75px;\"><head><base href=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\"/>\n<meta charset=\"utf-8\"/>\n<meta content=\"width=device-width, initial-scale=1\" name=\"viewport\"/>\n<link href=\"https://gmpg.org/xfn/11\" rel=\"profile\"/>\n<link href=\"https://github.githubassets.com/favicon.ico\" rel=\"icon\" type=\"image/x-icon\"/>\n<meta content=\"index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1\" name=\"robots\"/>\n<!-- This site is optimized with the Yoast SEO Premium plugin v27.5 (Yoast SEO v27.5) - https://yoast.com/product/yoast-seo-premium-wordpress/ -->\n<title>Still a developer. Just outside. Our latest GitHub Shop collection is here. - The GitHub Blog</title>\n<meta content=\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\" name=\"description\"/>\n<link href=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\" rel=\"canonical\"/>\n<meta content=\"en_US\" property=\"og:locale\"/>\n<meta content=\"article\" property=\"og:type\"/>\n<meta content=\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\" property=\"og:title\"/>\n<meta content=\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\" property=\"og:description\"/>\n<meta content=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\" property=\"og:url\"/>\n<meta content=\"The GitHub Blog\" property=\"og:site_name\"/>\n<meta content=\"2026-05-28T18:18:43+00:00\" property=\"article:published_time\"/>\n<meta content=\"2026-05-28T18:18:46+00:00\" property=\"article:modified_time\"/>\n<meta content=\"https://github.blog/wp-content/uploads/2026/05/Blog-Header.png\" property=\"og:image\"/>\n<meta content=\"2286\" property=\"og:image:width\"/>\n<meta content=\"1210\" property=\"og:image:height\"/>\n<meta content=\"image/png\" property=\"og:image:type\"/>\n<meta content=\"Lavinia Sfetcu\" name=\"author\"/>\n<meta content=\"summary_large_image\" name=\"twitter:card\"/>\n<meta content=\"Written by\" name=\"twitter:label1\"/>\n<meta content=\"Lavinia Sfetcu\" name=\"twitter:data1\"/>\n<meta content=\"Est. reading time\" name=\"twitter:label2\"/>\n<meta content=\"3 minutes\" name=\"twitter:data2\"/>\n\n\n\n<!-- / Yoast SEO Premium plugin. -->\n<link href=\"//ghcc.githubassets.com\" rel=\"dns-prefetch\"/>\n<link href=\"//js.monitor.azure.com\" rel=\"dns-prefetch\"/>\n<link href=\"//cdnjs.cloudflare.com\" rel=\"dns-prefetch\"/>\n<link href=\"//analytics.githubassets.com\" rel=\"dns-prefetch\"/>\n<link href=\"//stats.wp.com\" rel=\"dns-prefetch\"/>\n<link href=\"//v0.wordpress.com\" rel=\"dns-prefetch\"/>\n<link href=\"https://github.blog/feed/\" rel=\"alternate\" title=\"The GitHub Blog » Feed\" type=\"application/rss+xml\"/>\n<link href=\"https://github.blog/comments/feed/\" rel=\"alternate\" title=\"The GitHub Blog » Comments Feed\" type=\"application/rss+xml\"/>\n<link href=\"https://github.blog/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fgithub.blog%2Fnews-insights%2Fcompany-news%2Fstill-a-developer-just-outside-our-latest-github-shop-collection-is-here%2F\" rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\"/>\n<link href=\"https://github.blog/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fgithub.blog%2Fnews-insights%2Fcompany-news%2Fstill-a-developer-just-outside-our-latest-github-shop-collection-is-here%2F&amp;format=xml\" rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\"/>\n<style id=\"wp-img-auto-sizes-contain-inline-css\">\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id=\"wp-emoji-styles-inline-css\">\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id=\"wp-block-library-inline-css\">\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}.has-text-align-center{text-align:center}.has-text-align-left{text-align:left}.has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;word-wrap:normal!important}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style>\n<style id=\"wp-block-button-inline-css\">\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://github.blog/wp-includes/blocks/button/style.min.css */\n</style>\n<style id=\"wp-block-heading-inline-css\">\nh1:where(.wp-block-heading).has-background,h2:where(.wp-block-heading).has-background,h3:where(.wp-block-heading).has-background,h4:where(.wp-block-heading).has-background,h5:where(.wp-block-heading).has-background,h6:where(.wp-block-heading).has-background{padding:1.25em 2.375em}h1.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h1.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h2.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h2.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h3.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h3.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h4.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h4.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h5.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h5.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h6.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h6.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]){rotate:180deg}\n/*# sourceURL=https://github.blog/wp-includes/blocks/heading/style.min.css */\n</style>\n<style id=\"wp-block-image-inline-css\">\n.wp-block-image>a,.wp-block-image>figure>a{display:inline-block}.wp-block-image img{box-sizing:border-box;height:auto;max-width:100%;vertical-align:bottom}@media not (prefers-reduced-motion){.wp-block-image img.hide{visibility:hidden}.wp-block-image img.show{animation:show-content-image .4s}}.wp-block-image[style*=border-radius] img,.wp-block-image[style*=border-radius]>a{border-radius:inherit}.wp-block-image.has-custom-border img{box-sizing:border-box}.wp-block-image.aligncenter{text-align:center}.wp-block-image.alignfull>a,.wp-block-image.alignwide>a{width:100%}.wp-block-image.alignfull img,.wp-block-image.alignwide img{height:auto;width:100%}.wp-block-image .aligncenter,.wp-block-image .alignleft,.wp-block-image .alignright,.wp-block-image.aligncenter,.wp-block-image.alignleft,.wp-block-image.alignright{display:table}.wp-block-image .aligncenter>figcaption,.wp-block-image .alignleft>figcaption,.wp-block-image .alignright>figcaption,.wp-block-image.aligncenter>figcaption,.wp-block-image.alignleft>figcaption,.wp-block-image.alignright>figcaption{caption-side:bottom;display:table-caption}.wp-block-image .alignleft{float:left;margin:.5em 1em .5em 0}.wp-block-image .alignright{float:right;margin:.5em 0 .5em 1em}.wp-block-image .aligncenter{margin-left:auto;margin-right:auto}.wp-block-image :where(figcaption){margin-bottom:1em;margin-top:.5em}.wp-block-image.is-style-circle-mask img{border-radius:9999px}@supports ((-webkit-mask-image:none) or (mask-image:none)) or (-webkit-mask-image:none){.wp-block-image.is-style-circle-mask img{border-radius:0;-webkit-mask-image:url('data:image/svg+xml;utf8,<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>');mask-mode:alpha;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}}:root :where(.wp-block-image.is-style-rounded img,.wp-block-image .is-style-rounded img){border-radius:9999px}.wp-block-image figure{margin:0}.wp-lightbox-container{display:flex;flex-direction:column;position:relative}.wp-lightbox-container img{cursor:zoom-in}.wp-lightbox-container img:hover+button{opacity:1}.wp-lightbox-container button{align-items:center;backdrop-filter:blur(16px) saturate(180%);background-color:#5a5a5a40;border:none;border-radius:4px;cursor:zoom-in;display:flex;height:20px;justify-content:center;opacity:0;padding:0;position:absolute;right:16px;text-align:center;top:16px;width:20px;z-index:100}@media not (prefers-reduced-motion){.wp-lightbox-container button{transition:opacity .2s ease}}.wp-lightbox-container button:focus-visible{outline:3px auto #5a5a5a40;outline:3px auto -webkit-focus-ring-color;outline-offset:3px}.wp-lightbox-container button:hover{cursor:pointer;opacity:1}.wp-lightbox-container button:focus{opacity:1}.wp-lightbox-container button:focus,.wp-lightbox-container button:hover,.wp-lightbox-container button:not(:hover):not(:active):not(.has-background){background-color:#5a5a5a40;border:none}.wp-lightbox-overlay{box-sizing:border-box;cursor:zoom-out;height:100vh;left:0;overflow:hidden;position:fixed;top:0;visibility:hidden;width:100%;z-index:100000}.wp-lightbox-overlay .close-button{align-items:center;cursor:pointer;display:flex;justify-content:center;min-height:40px;min-width:40px;padding:0;position:absolute;right:calc(env(safe-area-inset-right) + 16px);top:calc(env(safe-area-inset-top) + 16px);z-index:5000000}.wp-lightbox-overlay .close-button:focus,.wp-lightbox-overlay .close-button:hover,.wp-lightbox-overlay .close-button:not(:hover):not(:active):not(.has-background){background:none;border:none}.wp-lightbox-overlay .lightbox-image-container{height:var(--wp--lightbox-container-height);left:50%;overflow:hidden;position:absolute;top:50%;transform:translate(-50%,-50%);transform-origin:top left;width:var(--wp--lightbox-container-width);z-index:9999999999}.wp-lightbox-overlay .wp-block-image{align-items:center;box-sizing:border-box;display:flex;height:100%;justify-content:center;margin:0;position:relative;transform-origin:0 0;width:100%;z-index:3000000}.wp-lightbox-overlay .wp-block-image img{height:var(--wp--lightbox-image-height);min-height:var(--wp--lightbox-image-height);min-width:var(--wp--lightbox-image-width);width:var(--wp--lightbox-image-width)}.wp-lightbox-overlay .wp-block-image figcaption{display:none}.wp-lightbox-overlay button{background:none;border:none}.wp-lightbox-overlay .scrim{background-color:#fff;height:100%;opacity:.9;position:absolute;width:100%;z-index:2000000}.wp-lightbox-overlay.active{visibility:visible}@media not (prefers-reduced-motion){.wp-lightbox-overlay.active{animation:turn-on-visibility .25s both}.wp-lightbox-overlay.active img{animation:turn-on-visibility .35s both}.wp-lightbox-overlay.show-closing-animation:not(.active){animation:turn-off-visibility .35s both}.wp-lightbox-overlay.show-closing-animation:not(.active) img{animation:turn-off-visibility .25s both}.wp-lightbox-overlay.zoom.active{animation:none;opacity:1;visibility:visible}.wp-lightbox-overlay.zoom.active .lightbox-image-container{animation:lightbox-zoom-in .4s}.wp-lightbox-overlay.zoom.active .lightbox-image-container img{animation:none}.wp-lightbox-overlay.zoom.active .scrim{animation:turn-on-visibility .4s forwards}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active){animation:none}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .lightbox-image-container{animation:lightbox-zoom-out .4s}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .lightbox-image-container img{animation:none}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .scrim{animation:turn-off-visibility .4s forwards}}@keyframes show-content-image{0%{visibility:hidden}99%{visibility:hidden}to{visibility:visible}}@keyframes turn-on-visibility{0%{opacity:0}to{opacity:1}}@keyframes turn-off-visibility{0%{opacity:1;visibility:visible}99%{opacity:0;visibility:visible}to{opacity:0;visibility:hidden}}@keyframes lightbox-zoom-in{0%{transform:translate(calc((-100vw + var(--wp--lightbox-scrollbar-width))/2 + var(--wp--lightbox-initial-left-position)),calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale))}to{transform:translate(-50%,-50%) scale(1)}}@keyframes lightbox-zoom-out{0%{transform:translate(-50%,-50%) scale(1);visibility:visible}99%{visibility:visible}to{transform:translate(calc((-100vw + var(--wp--lightbox-scrollbar-width))/2 + var(--wp--lightbox-initial-left-position)),calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale));visibility:hidden}}\n/*# sourceURL=https://github.blog/wp-includes/blocks/image/style.min.css */\n</style>\n<style id=\"wp-block-buttons-inline-css\">\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://github.blog/wp-includes/blocks/buttons/style.min.css */\n</style>\n<style id=\"wp-block-paragraph-inline-css\">\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://github.blog/wp-includes/blocks/paragraph/style.min.css */\n</style>\n<style id=\"github-typography-presets-inline-css\">\n.is-typography-preset-h1{font-size:var(--base-size-36, 36px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:0;}.is-typography-preset-h2{font-size:var(--base-size-32, 32px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:40px;}.is-typography-preset-h3{font-size:var(--base-size-28, 28px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h4{font-size:var(--base-size-24, 24px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h5{font-size:var(--base-size-20, 20px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h6{font-size:var(--base-size-18, 18px);font-weight:700;line-height:1.25;margin-block-end:0;margin-block-start:20px;}.is-typography-preset-display{font:var(--text-display-shorthand);}.is-typography-preset-title-large{font:var(--text-title-shorthand-large);}.is-typography-preset-title-medium{font:var(--text-title-shorthand-medium);}.is-typography-preset-title-small{font:var(--text-title-shorthand-small);}.is-typography-preset-subtitle{font:var(--text-subtitle-shorthand);}.is-typography-preset-body-large{font:var(--text-body-shorthand-large);}.is-typography-preset-body-medium{font:var(--text-body-shorthand-medium);}.is-typography-preset-body-small{font:var(--text-body-shorthand-small);}\n/*# sourceURL=github-typography-presets-inline-css */\n</style>\n<style id=\"global-styles-inline-css\">\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--spacing--20: var(--base-size-20, 1.25rem);--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: var(--base-size-40, 2.5rem);--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: var(--base-size-80, 5rem);--wp--preset--spacing--4: var(--base-size-4, 0.25rem);--wp--preset--spacing--6: var(--base-size-6, 0.375rem);--wp--preset--spacing--8: var(--base-size-8, 0.5rem);--wp--preset--spacing--2: var(--base-size-2, 0.125rem);--wp--preset--spacing--12: var(--base-size-12, 0.75rem);--wp--preset--spacing--16: var(--base-size-16, 1rem);--wp--preset--spacing--24: var(--base-size-24, 1.5rem);--wp--preset--spacing--28: var(--base-size-28, 1.75rem);--wp--preset--spacing--32: var(--base-size-32, 2rem);--wp--preset--spacing--36: var(--base-size-36, 2.25rem);--wp--preset--spacing--44: var(--base-size-44, 2.75rem);--wp--preset--spacing--48: var(--base-size-48, 3rem);--wp--preset--spacing--64: var(--base-size-64, 4rem);--wp--preset--spacing--96: var(--base-size-96, 6rem);--wp--preset--spacing--112: var(--base-size-112, 7rem);--wp--preset--spacing--128: var(--base-size-128, 8rem);--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--typography--presets--0--name: Like h1;--wp--custom--typography--presets--0--slug: h1;--wp--custom--typography--presets--0--font-size: var(--base-size-36, 36px);--wp--custom--typography--presets--0--font-weight: 700;--wp--custom--typography--presets--0--line-height: 1.25;--wp--custom--typography--presets--0--margin-block-end: 20px;--wp--custom--typography--presets--0--margin-block-start: 0;--wp--custom--typography--presets--1--name: Like h2;--wp--custom--typography--presets--1--slug: h2;--wp--custom--typography--presets--1--font-size: var(--base-size-32, 32px);--wp--custom--typography--presets--1--font-weight: 700;--wp--custom--typography--presets--1--line-height: 1.25;--wp--custom--typography--presets--1--margin-block-end: 20px;--wp--custom--typography--presets--1--margin-block-start: 40px;--wp--custom--typography--presets--2--name: Like h3;--wp--custom--typography--presets--2--slug: h3;--wp--custom--typography--presets--2--font-size: var(--base-size-28, 28px);--wp--custom--typography--presets--2--font-weight: 700;--wp--custom--typography--presets--2--line-height: 1.25;--wp--custom--typography--presets--2--margin-block-end: 12px;--wp--custom--typography--presets--2--margin-block-start: 20px;--wp--custom--typography--presets--3--name: Like h4;--wp--custom--typography--presets--3--slug: h4;--wp--custom--typography--presets--3--font-size: var(--base-size-24, 24px);--wp--custom--typography--presets--3--font-weight: 700;--wp--custom--typography--presets--3--line-height: 1.25;--wp--custom--typography--presets--3--margin-block-end: 12px;--wp--custom--typography--presets--3--margin-block-start: 20px;--wp--custom--typography--presets--4--name: Like h5;--wp--custom--typography--presets--4--slug: h5;--wp--custom--typography--presets--4--font-size: var(--base-size-20, 20px);--wp--custom--typography--presets--4--font-weight: 700;--wp--custom--typography--presets--4--line-height: 1.25;--wp--custom--typography--presets--4--margin-block-end: 12px;--wp--custom--typography--presets--4--margin-block-start: 20px;--wp--custom--typography--presets--5--name: Like h6;--wp--custom--typography--presets--5--slug: h6;--wp--custom--typography--presets--5--font-size: var(--base-size-18, 18px);--wp--custom--typography--presets--5--font-weight: 700;--wp--custom--typography--presets--5--line-height: 1.25;--wp--custom--typography--presets--5--margin-block-end: 0;--wp--custom--typography--presets--5--margin-block-start: 20px;--wp--custom--typography--presets--6--name: Display;--wp--custom--typography--presets--6--slug: display;--wp--custom--typography--presets--6--font: var(--text-display-shorthand);--wp--custom--typography--presets--7--name: Title Large;--wp--custom--typography--presets--7--slug: title-large;--wp--custom--typography--presets--7--font: var(--text-title-shorthand-large);--wp--custom--typography--presets--8--name: Title Medium;--wp--custom--typography--presets--8--slug: title-medium;--wp--custom--typography--presets--8--font: var(--text-title-shorthand-medium);--wp--custom--typography--presets--9--name: Title Small;--wp--custom--typography--presets--9--slug: title-small;--wp--custom--typography--presets--9--font: var(--text-title-shorthand-small);--wp--custom--typography--presets--10--name: Subtitle;--wp--custom--typography--presets--10--slug: subtitle;--wp--custom--typography--presets--10--font: var(--text-subtitle-shorthand);--wp--custom--typography--presets--11--name: Body Large;--wp--custom--typography--presets--11--slug: body-large;--wp--custom--typography--presets--11--font: var(--text-body-shorthand-large);--wp--custom--typography--presets--12--name: Body Medium;--wp--custom--typography--presets--12--slug: body-medium;--wp--custom--typography--presets--12--font: var(--text-body-shorthand-medium);--wp--custom--typography--presets--13--name: Body Small;--wp--custom--typography--presets--13--slug: body-small;--wp--custom--typography--presets--13--font: var(--text-body-shorthand-small);}:root { --wp--style--global--content-size: calc(1280px - var(--p-responsive-blog) * 2);--wp--style--global--wide-size: calc(1280px - var(--p-responsive-blog) * 2); }:where(body) { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) { padding-right: 0; padding-left: 0; }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) > .alignfull { margin-left: 0; margin-right: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{font-family: var(--brand-fontStack-sansSerif, sans-serif);--wp--style--root--padding-top: 0px;--wp--style--root--padding-right: var(--p-responsive-blog);--wp--style--root--padding-bottom: 0px;--wp--style--root--padding-left: var(--p-responsive-blog);}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n<link href=\"file:///var/www/rss-offline/assets/c05e51dd412952e4/59be3494fbcb9f18.css\" id=\"all-css-8\" media=\"all\" rel=\"stylesheet\" type=\"text/css\"/>\n<link href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css?ver=11.4.0\" id=\"highlightjs-css-css\" media=\"all\" rel=\"stylesheet\"/>\n<link href=\"https://github.blog/wp-json/\" rel=\"https://api.w.org/\"/><link href=\"https://github.blog/wp-json/wp/v2/posts/96339\" rel=\"alternate\" title=\"JSON\" type=\"application/json\"/><link href=\"https://github.blog/xmlrpc.php?rsd\" rel=\"EditURI\" title=\"RSD\" type=\"application/rsd+xml\"/>\n<meta content=\"WordPress 6.9.4\" name=\"generator\"/>\n<link href=\"https://wp.me/pamS32-p3R\" rel=\"shortlink\"/>\n<!-- Stream WordPress user activity plugin v4.1.2 -->\n<style>img#wpstats{display:none}</style>\n<link href=\"https://github.blog/llms.txt\" rel=\"llms-sitemap\"/>\n<meta content=\"https://collector.githubapp.com/github-blog/collect\" name=\"ha-url\"/><link as=\"font\" crossorigin=\"anonymous\" href=\"https://github.blog/wp-content/themes/github-2021/dist/fonts/mona-sans.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"anonymous\" href=\"https://github.blog/wp-content/themes/github-2021/dist/fonts/MonaspaceNeon-Regular.woff2\" rel=\"preload\" type=\"font/woff2\"/><link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=32%2C32\" rel=\"icon\" sizes=\"32x32\"/>\n<link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=192%2C192\" rel=\"icon\" sizes=\"192x192\"/>\n<link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=180%2C180\" rel=\"apple-touch-icon\"/>\n<meta content=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=270%2C270\" name=\"msapplication-TileImage\"/>\n<style id=\"core-block-supports-inline-css\">\n.wp-container-core-buttons-is-layout-a89b3969{justify-content:center;}\n/*# sourceURL=core-block-supports-inline-css */\n</style>\n<style id=\"wp-style-engine-github-typography-presets-inline-css\">\n.is-typography-preset-h1{font-size:var(--base-size-36, 36px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:0;}.is-typography-preset-h2{font-size:var(--base-size-32, 32px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:40px;}.is-typography-preset-h3{font-size:var(--base-size-28, 28px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h4{font-size:var(--base-size-24, 24px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h5{font-size:var(--base-size-20, 20px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h6{font-size:var(--base-size-18, 18px);font-weight:700;line-height:1.25;margin-block-end:0;margin-block-start:20px;}.is-typography-preset-display{font:var(--text-display-shorthand);}.is-typography-preset-title-large{font:var(--text-title-shorthand-large);}.is-typography-preset-title-medium{font:var(--text-title-shorthand-medium);}.is-typography-preset-title-small{font:var(--text-title-shorthand-small);}.is-typography-preset-subtitle{font:var(--text-subtitle-shorthand);}.is-typography-preset-body-large{font:var(--text-body-shorthand-large);}.is-typography-preset-body-medium{font:var(--text-body-shorthand-medium);}.is-typography-preset-body-small{font:var(--text-body-shorthand-small);}\n/*# sourceURL=wp-style-engine-github-typography-presets-inline-css */\n</style>\n\n[Showing lines 1-118 of 979 (50.0KB limit). Use offset=119 to continue.]"}],"details":{"truncation":{"content":"<!DOCTYPE html>\n\n<!DOCTYPE html>\n\n<html class=\"mt-0\" lang=\"en-US\" style=\"--navigation-offset: 0px; --header-offset: 75px;\"><head><base href=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\"/>\n<meta charset=\"utf-8\"/>\n<meta content=\"width=device-width, initial-scale=1\" name=\"viewport\"/>\n<link href=\"https://gmpg.org/xfn/11\" rel=\"profile\"/>\n<link href=\"https://github.githubassets.com/favicon.ico\" rel=\"icon\" type=\"image/x-icon\"/>\n<meta content=\"index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1\" name=\"robots\"/>\n<!-- This site is optimized with the Yoast SEO Premium plugin v27.5 (Yoast SEO v27.5) - https://yoast.com/product/yoast-seo-premium-wordpress/ -->\n<title>Still a developer. Just outside. Our latest GitHub Shop collection is here. - The GitHub Blog</title>\n<meta content=\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\" name=\"description\"/>\n<link href=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\" rel=\"canonical\"/>\n<meta content=\"en_US\" property=\"og:locale\"/>\n<meta content=\"article\" property=\"og:type\"/>\n<meta content=\"Still a developer. Just outside. Our latest GitHub Shop collection is here.\" property=\"og:title\"/>\n<meta content=\"The ESC collection lets you escape the confines of your desk and get out into the sun where good ideas are bound to happen.\" property=\"og:description\"/>\n<meta content=\"https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\" property=\"og:url\"/>\n<meta content=\"The GitHub Blog\" property=\"og:site_name\"/>\n<meta content=\"2026-05-28T18:18:43+00:00\" property=\"article:published_time\"/>\n<meta content=\"2026-05-28T18:18:46+00:00\" property=\"article:modified_time\"/>\n<meta content=\"https://github.blog/wp-content/uploads/2026/05/Blog-Header.png\" property=\"og:image\"/>\n<meta content=\"2286\" property=\"og:image:width\"/>\n<meta content=\"1210\" property=\"og:image:height\"/>\n<meta content=\"image/png\" property=\"og:image:type\"/>\n<meta content=\"Lavinia Sfetcu\" name=\"author\"/>\n<meta content=\"summary_large_image\" name=\"twitter:card\"/>\n<meta content=\"Written by\" name=\"twitter:label1\"/>\n<meta content=\"Lavinia Sfetcu\" name=\"twitter:data1\"/>\n<meta content=\"Est. reading time\" name=\"twitter:label2\"/>\n<meta content=\"3 minutes\" name=\"twitter:data2\"/>\n\n\n\n<!-- / Yoast SEO Premium plugin. -->\n<link href=\"//ghcc.githubassets.com\" rel=\"dns-prefetch\"/>\n<link href=\"//js.monitor.azure.com\" rel=\"dns-prefetch\"/>\n<link href=\"//cdnjs.cloudflare.com\" rel=\"dns-prefetch\"/>\n<link href=\"//analytics.githubassets.com\" rel=\"dns-prefetch\"/>\n<link href=\"//stats.wp.com\" rel=\"dns-prefetch\"/>\n<link href=\"//v0.wordpress.com\" rel=\"dns-prefetch\"/>\n<link href=\"https://github.blog/feed/\" rel=\"alternate\" title=\"The GitHub Blog » Feed\" type=\"application/rss+xml\"/>\n<link href=\"https://github.blog/comments/feed/\" rel=\"alternate\" title=\"The GitHub Blog » Comments Feed\" type=\"application/rss+xml\"/>\n<link href=\"https://github.blog/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fgithub.blog%2Fnews-insights%2Fcompany-news%2Fstill-a-developer-just-outside-our-latest-github-shop-collection-is-here%2F\" rel=\"alternate\" title=\"oEmbed (JSON)\" type=\"application/json+oembed\"/>\n<link href=\"https://github.blog/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fgithub.blog%2Fnews-insights%2Fcompany-news%2Fstill-a-developer-just-outside-our-latest-github-shop-collection-is-here%2F&amp;format=xml\" rel=\"alternate\" title=\"oEmbed (XML)\" type=\"text/xml+oembed\"/>\n<style id=\"wp-img-auto-sizes-contain-inline-css\">\nimg:is([sizes=auto i],[sizes^=\"auto,\" i]){contain-intrinsic-size:3000px 1500px}\n/*# sourceURL=wp-img-auto-sizes-contain-inline-css */\n</style>\n<style id=\"wp-emoji-styles-inline-css\">\n\n\timg.wp-smiley, img.emoji {\n\t\tdisplay: inline !important;\n\t\tborder: none !important;\n\t\tbox-shadow: none !important;\n\t\theight: 1em !important;\n\t\twidth: 1em !important;\n\t\tmargin: 0 0.07em !important;\n\t\tvertical-align: -0.1em !important;\n\t\tbackground: none !important;\n\t\tpadding: 0 !important;\n\t}\n/*# sourceURL=wp-emoji-styles-inline-css */\n</style>\n<style id=\"wp-block-library-inline-css\">\n:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}.has-text-align-center{text-align:center}.has-text-align-left{text-align:left}.has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;word-wrap:normal!important}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style*=border-top-color]){border-top-style:solid}html :where([style*=border-right-color]){border-right-style:solid}html :where([style*=border-bottom-color]){border-bottom-style:solid}html :where([style*=border-left-color]){border-left-style:solid}html :where([style*=border-width]){border-style:solid}html :where([style*=border-top-width]){border-top-style:solid}html :where([style*=border-right-width]){border-right-style:solid}html :where([style*=border-bottom-width]){border-bottom-style:solid}html :where([style*=border-left-width]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}\n\n/*# sourceURL=wp-block-library-inline-css */\n</style>\n<style id=\"wp-block-button-inline-css\">\n.wp-block-button__link{align-content:center;box-sizing:border-box;cursor:pointer;display:inline-block;height:100%;text-align:center;word-break:break-word}.wp-block-button__link.aligncenter{text-align:center}.wp-block-button__link.alignright{text-align:right}:where(.wp-block-button__link){border-radius:9999px;box-shadow:none;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-button[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons>.wp-block-button.has-custom-width{max-width:none}.wp-block-buttons>.wp-block-button.has-custom-width .wp-block-button__link{width:100%}.wp-block-buttons>.wp-block-button.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons>.wp-block-button.wp-block-button__width-25{width:calc(25% - var(--wp--style--block-gap, .5em)*.75)}.wp-block-buttons>.wp-block-button.wp-block-button__width-50{width:calc(50% - var(--wp--style--block-gap, .5em)*.5)}.wp-block-buttons>.wp-block-button.wp-block-button__width-75{width:calc(75% - var(--wp--style--block-gap, .5em)*.25)}.wp-block-buttons>.wp-block-button.wp-block-button__width-100{flex-basis:100%;width:100%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-25{width:25%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-50{width:50%}.wp-block-buttons.is-vertical>.wp-block-button.wp-block-button__width-75{width:75%}.wp-block-button.is-style-squared,.wp-block-button__link.wp-block-button.is-style-squared{border-radius:0}.wp-block-button.no-border-radius,.wp-block-button__link.no-border-radius{border-radius:0!important}:root :where(.wp-block-button .wp-block-button__link.is-style-outline),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link){border:2px solid;padding:.667em 1.333em}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-text-color)){color:currentColor}:root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-background)),:root :where(.wp-block-button.is-style-outline>.wp-block-button__link:not(.has-background)){background-color:initial;background-image:none}\n/*# sourceURL=https://github.blog/wp-includes/blocks/button/style.min.css */\n</style>\n<style id=\"wp-block-heading-inline-css\">\nh1:where(.wp-block-heading).has-background,h2:where(.wp-block-heading).has-background,h3:where(.wp-block-heading).has-background,h4:where(.wp-block-heading).has-background,h5:where(.wp-block-heading).has-background,h6:where(.wp-block-heading).has-background{padding:1.25em 2.375em}h1.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h1.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h2.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h2.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h3.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h3.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h4.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h4.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h5.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h5.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]),h6.has-text-align-left[style*=writing-mode]:where([style*=vertical-lr]),h6.has-text-align-right[style*=writing-mode]:where([style*=vertical-rl]){rotate:180deg}\n/*# sourceURL=https://github.blog/wp-includes/blocks/heading/style.min.css */\n</style>\n<style id=\"wp-block-image-inline-css\">\n.wp-block-image>a,.wp-block-image>figure>a{display:inline-block}.wp-block-image img{box-sizing:border-box;height:auto;max-width:100%;vertical-align:bottom}@media not (prefers-reduced-motion){.wp-block-image img.hide{visibility:hidden}.wp-block-image img.show{animation:show-content-image .4s}}.wp-block-image[style*=border-radius] img,.wp-block-image[style*=border-radius]>a{border-radius:inherit}.wp-block-image.has-custom-border img{box-sizing:border-box}.wp-block-image.aligncenter{text-align:center}.wp-block-image.alignfull>a,.wp-block-image.alignwide>a{width:100%}.wp-block-image.alignfull img,.wp-block-image.alignwide img{height:auto;width:100%}.wp-block-image .aligncenter,.wp-block-image .alignleft,.wp-block-image .alignright,.wp-block-image.aligncenter,.wp-block-image.alignleft,.wp-block-image.alignright{display:table}.wp-block-image .aligncenter>figcaption,.wp-block-image .alignleft>figcaption,.wp-block-image .alignright>figcaption,.wp-block-image.aligncenter>figcaption,.wp-block-image.alignleft>figcaption,.wp-block-image.alignright>figcaption{caption-side:bottom;display:table-caption}.wp-block-image .alignleft{float:left;margin:.5em 1em .5em 0}.wp-block-image .alignright{float:right;margin:.5em 0 .5em 1em}.wp-block-image .aligncenter{margin-left:auto;margin-right:auto}.wp-block-image :where(figcaption){margin-bottom:1em;margin-top:.5em}.wp-block-image.is-style-circle-mask img{border-radius:9999px}@supports ((-webkit-mask-image:none) or (mask-image:none)) or (-webkit-mask-image:none){.wp-block-image.is-style-circle-mask img{border-radius:0;-webkit-mask-image:url('data:image/svg+xml;utf8,<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"50\" cy=\"50\" r=\"50\"/></svg>');mask-mode:alpha;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain}}:root :where(.wp-block-image.is-style-rounded img,.wp-block-image .is-style-rounded img){border-radius:9999px}.wp-block-image figure{margin:0}.wp-lightbox-container{display:flex;flex-direction:column;position:relative}.wp-lightbox-container img{cursor:zoom-in}.wp-lightbox-container img:hover+button{opacity:1}.wp-lightbox-container button{align-items:center;backdrop-filter:blur(16px) saturate(180%);background-color:#5a5a5a40;border:none;border-radius:4px;cursor:zoom-in;display:flex;height:20px;justify-content:center;opacity:0;padding:0;position:absolute;right:16px;text-align:center;top:16px;width:20px;z-index:100}@media not (prefers-reduced-motion){.wp-lightbox-container button{transition:opacity .2s ease}}.wp-lightbox-container button:focus-visible{outline:3px auto #5a5a5a40;outline:3px auto -webkit-focus-ring-color;outline-offset:3px}.wp-lightbox-container button:hover{cursor:pointer;opacity:1}.wp-lightbox-container button:focus{opacity:1}.wp-lightbox-container button:focus,.wp-lightbox-container button:hover,.wp-lightbox-container button:not(:hover):not(:active):not(.has-background){background-color:#5a5a5a40;border:none}.wp-lightbox-overlay{box-sizing:border-box;cursor:zoom-out;height:100vh;left:0;overflow:hidden;position:fixed;top:0;visibility:hidden;width:100%;z-index:100000}.wp-lightbox-overlay .close-button{align-items:center;cursor:pointer;display:flex;justify-content:center;min-height:40px;min-width:40px;padding:0;position:absolute;right:calc(env(safe-area-inset-right) + 16px);top:calc(env(safe-area-inset-top) + 16px);z-index:5000000}.wp-lightbox-overlay .close-button:focus,.wp-lightbox-overlay .close-button:hover,.wp-lightbox-overlay .close-button:not(:hover):not(:active):not(.has-background){background:none;border:none}.wp-lightbox-overlay .lightbox-image-container{height:var(--wp--lightbox-container-height);left:50%;overflow:hidden;position:absolute;top:50%;transform:translate(-50%,-50%);transform-origin:top left;width:var(--wp--lightbox-container-width);z-index:9999999999}.wp-lightbox-overlay .wp-block-image{align-items:center;box-sizing:border-box;display:flex;height:100%;justify-content:center;margin:0;position:relative;transform-origin:0 0;width:100%;z-index:3000000}.wp-lightbox-overlay .wp-block-image img{height:var(--wp--lightbox-image-height);min-height:var(--wp--lightbox-image-height);min-width:var(--wp--lightbox-image-width);width:var(--wp--lightbox-image-width)}.wp-lightbox-overlay .wp-block-image figcaption{display:none}.wp-lightbox-overlay button{background:none;border:none}.wp-lightbox-overlay .scrim{background-color:#fff;height:100%;opacity:.9;position:absolute;width:100%;z-index:2000000}.wp-lightbox-overlay.active{visibility:visible}@media not (prefers-reduced-motion){.wp-lightbox-overlay.active{animation:turn-on-visibility .25s both}.wp-lightbox-overlay.active img{animation:turn-on-visibility .35s both}.wp-lightbox-overlay.show-closing-animation:not(.active){animation:turn-off-visibility .35s both}.wp-lightbox-overlay.show-closing-animation:not(.active) img{animation:turn-off-visibility .25s both}.wp-lightbox-overlay.zoom.active{animation:none;opacity:1;visibility:visible}.wp-lightbox-overlay.zoom.active .lightbox-image-container{animation:lightbox-zoom-in .4s}.wp-lightbox-overlay.zoom.active .lightbox-image-container img{animation:none}.wp-lightbox-overlay.zoom.active .scrim{animation:turn-on-visibility .4s forwards}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active){animation:none}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .lightbox-image-container{animation:lightbox-zoom-out .4s}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .lightbox-image-container img{animation:none}.wp-lightbox-overlay.zoom.show-closing-animation:not(.active) .scrim{animation:turn-off-visibility .4s forwards}}@keyframes show-content-image{0%{visibility:hidden}99%{visibility:hidden}to{visibility:visible}}@keyframes turn-on-visibility{0%{opacity:0}to{opacity:1}}@keyframes turn-off-visibility{0%{opacity:1;visibility:visible}99%{opacity:0;visibility:visible}to{opacity:0;visibility:hidden}}@keyframes lightbox-zoom-in{0%{transform:translate(calc((-100vw + var(--wp--lightbox-scrollbar-width))/2 + var(--wp--lightbox-initial-left-position)),calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale))}to{transform:translate(-50%,-50%) scale(1)}}@keyframes lightbox-zoom-out{0%{transform:translate(-50%,-50%) scale(1);visibility:visible}99%{visibility:visible}to{transform:translate(calc((-100vw + var(--wp--lightbox-scrollbar-width))/2 + var(--wp--lightbox-initial-left-position)),calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale));visibility:hidden}}\n/*# sourceURL=https://github.blog/wp-includes/blocks/image/style.min.css */\n</style>\n<style id=\"wp-block-buttons-inline-css\">\n.wp-block-buttons{box-sizing:border-box}.wp-block-buttons.is-vertical{flex-direction:column}.wp-block-buttons.is-vertical>.wp-block-button:last-child{margin-bottom:0}.wp-block-buttons>.wp-block-button{display:inline-block;margin:0}.wp-block-buttons.is-content-justification-left{justify-content:flex-start}.wp-block-buttons.is-content-justification-left.is-vertical{align-items:flex-start}.wp-block-buttons.is-content-justification-center{justify-content:center}.wp-block-buttons.is-content-justification-center.is-vertical{align-items:center}.wp-block-buttons.is-content-justification-right{justify-content:flex-end}.wp-block-buttons.is-content-justification-right.is-vertical{align-items:flex-end}.wp-block-buttons.is-content-justification-space-between{justify-content:space-between}.wp-block-buttons.aligncenter{text-align:center}.wp-block-buttons:not(.is-content-justification-space-between,.is-content-justification-right,.is-content-justification-left,.is-content-justification-center) .wp-block-button.aligncenter{margin-left:auto;margin-right:auto;width:100%}.wp-block-buttons[style*=text-decoration] .wp-block-button,.wp-block-buttons[style*=text-decoration] .wp-block-button__link{text-decoration:inherit}.wp-block-buttons.has-custom-font-size .wp-block-button__link{font-size:inherit}.wp-block-buttons .wp-block-button__link{width:100%}.wp-block-button.aligncenter{text-align:center}\n/*# sourceURL=https://github.blog/wp-includes/blocks/buttons/style.min.css */\n</style>\n<style id=\"wp-block-paragraph-inline-css\">\n.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*=\"writing-mode:vertical-lr\"],p.has-text-align-right[style*=\"writing-mode:vertical-rl\"]{rotate:180deg}\n/*# sourceURL=https://github.blog/wp-includes/blocks/paragraph/style.min.css */\n</style>\n<style id=\"github-typography-presets-inline-css\">\n.is-typography-preset-h1{font-size:var(--base-size-36, 36px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:0;}.is-typography-preset-h2{font-size:var(--base-size-32, 32px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:40px;}.is-typography-preset-h3{font-size:var(--base-size-28, 28px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h4{font-size:var(--base-size-24, 24px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h5{font-size:var(--base-size-20, 20px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h6{font-size:var(--base-size-18, 18px);font-weight:700;line-height:1.25;margin-block-end:0;margin-block-start:20px;}.is-typography-preset-display{font:var(--text-display-shorthand);}.is-typography-preset-title-large{font:var(--text-title-shorthand-large);}.is-typography-preset-title-medium{font:var(--text-title-shorthand-medium);}.is-typography-preset-title-small{font:var(--text-title-shorthand-small);}.is-typography-preset-subtitle{font:var(--text-subtitle-shorthand);}.is-typography-preset-body-large{font:var(--text-body-shorthand-large);}.is-typography-preset-body-medium{font:var(--text-body-shorthand-medium);}.is-typography-preset-body-small{font:var(--text-body-shorthand-small);}\n/*# sourceURL=github-typography-presets-inline-css */\n</style>\n<style id=\"global-styles-inline-css\">\n:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--spacing--20: var(--base-size-20, 1.25rem);--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: var(--base-size-40, 2.5rem);--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: var(--base-size-80, 5rem);--wp--preset--spacing--4: var(--base-size-4, 0.25rem);--wp--preset--spacing--6: var(--base-size-6, 0.375rem);--wp--preset--spacing--8: var(--base-size-8, 0.5rem);--wp--preset--spacing--2: var(--base-size-2, 0.125rem);--wp--preset--spacing--12: var(--base-size-12, 0.75rem);--wp--preset--spacing--16: var(--base-size-16, 1rem);--wp--preset--spacing--24: var(--base-size-24, 1.5rem);--wp--preset--spacing--28: var(--base-size-28, 1.75rem);--wp--preset--spacing--32: var(--base-size-32, 2rem);--wp--preset--spacing--36: var(--base-size-36, 2.25rem);--wp--preset--spacing--44: var(--base-size-44, 2.75rem);--wp--preset--spacing--48: var(--base-size-48, 3rem);--wp--preset--spacing--64: var(--base-size-64, 4rem);--wp--preset--spacing--96: var(--base-size-96, 6rem);--wp--preset--spacing--112: var(--base-size-112, 7rem);--wp--preset--spacing--128: var(--base-size-128, 8rem);--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);--wp--custom--typography--presets--0--name: Like h1;--wp--custom--typography--presets--0--slug: h1;--wp--custom--typography--presets--0--font-size: var(--base-size-36, 36px);--wp--custom--typography--presets--0--font-weight: 700;--wp--custom--typography--presets--0--line-height: 1.25;--wp--custom--typography--presets--0--margin-block-end: 20px;--wp--custom--typography--presets--0--margin-block-start: 0;--wp--custom--typography--presets--1--name: Like h2;--wp--custom--typography--presets--1--slug: h2;--wp--custom--typography--presets--1--font-size: var(--base-size-32, 32px);--wp--custom--typography--presets--1--font-weight: 700;--wp--custom--typography--presets--1--line-height: 1.25;--wp--custom--typography--presets--1--margin-block-end: 20px;--wp--custom--typography--presets--1--margin-block-start: 40px;--wp--custom--typography--presets--2--name: Like h3;--wp--custom--typography--presets--2--slug: h3;--wp--custom--typography--presets--2--font-size: var(--base-size-28, 28px);--wp--custom--typography--presets--2--font-weight: 700;--wp--custom--typography--presets--2--line-height: 1.25;--wp--custom--typography--presets--2--margin-block-end: 12px;--wp--custom--typography--presets--2--margin-block-start: 20px;--wp--custom--typography--presets--3--name: Like h4;--wp--custom--typography--presets--3--slug: h4;--wp--custom--typography--presets--3--font-size: var(--base-size-24, 24px);--wp--custom--typography--presets--3--font-weight: 700;--wp--custom--typography--presets--3--line-height: 1.25;--wp--custom--typography--presets--3--margin-block-end: 12px;--wp--custom--typography--presets--3--margin-block-start: 20px;--wp--custom--typography--presets--4--name: Like h5;--wp--custom--typography--presets--4--slug: h5;--wp--custom--typography--presets--4--font-size: var(--base-size-20, 20px);--wp--custom--typography--presets--4--font-weight: 700;--wp--custom--typography--presets--4--line-height: 1.25;--wp--custom--typography--presets--4--margin-block-end: 12px;--wp--custom--typography--presets--4--margin-block-start: 20px;--wp--custom--typography--presets--5--name: Like h6;--wp--custom--typography--presets--5--slug: h6;--wp--custom--typography--presets--5--font-size: var(--base-size-18, 18px);--wp--custom--typography--presets--5--font-weight: 700;--wp--custom--typography--presets--5--line-height: 1.25;--wp--custom--typography--presets--5--margin-block-end: 0;--wp--custom--typography--presets--5--margin-block-start: 20px;--wp--custom--typography--presets--6--name: Display;--wp--custom--typography--presets--6--slug: display;--wp--custom--typography--presets--6--font: var(--text-display-shorthand);--wp--custom--typography--presets--7--name: Title Large;--wp--custom--typography--presets--7--slug: title-large;--wp--custom--typography--presets--7--font: var(--text-title-shorthand-large);--wp--custom--typography--presets--8--name: Title Medium;--wp--custom--typography--presets--8--slug: title-medium;--wp--custom--typography--presets--8--font: var(--text-title-shorthand-medium);--wp--custom--typography--presets--9--name: Title Small;--wp--custom--typography--presets--9--slug: title-small;--wp--custom--typography--presets--9--font: var(--text-title-shorthand-small);--wp--custom--typography--presets--10--name: Subtitle;--wp--custom--typography--presets--10--slug: subtitle;--wp--custom--typography--presets--10--font: var(--text-subtitle-shorthand);--wp--custom--typography--presets--11--name: Body Large;--wp--custom--typography--presets--11--slug: body-large;--wp--custom--typography--presets--11--font: var(--text-body-shorthand-large);--wp--custom--typography--presets--12--name: Body Medium;--wp--custom--typography--presets--12--slug: body-medium;--wp--custom--typography--presets--12--font: var(--text-body-shorthand-medium);--wp--custom--typography--presets--13--name: Body Small;--wp--custom--typography--presets--13--slug: body-small;--wp--custom--typography--presets--13--font: var(--text-body-shorthand-small);}:root { --wp--style--global--content-size: calc(1280px - var(--p-responsive-blog) * 2);--wp--style--global--wide-size: calc(1280px - var(--p-responsive-blog) * 2); }:where(body) { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) { padding-right: 0; padding-left: 0; }.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) > .alignfull { margin-left: 0; margin-right: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{font-family: var(--brand-fontStack-sansSerif, sans-serif);--wp--style--root--padding-top: 0px;--wp--style--root--padding-right: var(--p-responsive-blog);--wp--style--root--padding-bottom: 0px;--wp--style--root--padding-left: var(--p-responsive-blog);}a:where(:not(.wp-element-button)){text-decoration: underline;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}\n/*# sourceURL=global-styles-inline-css */\n</style>\n<link href=\"file:///var/www/rss-offline/assets/c05e51dd412952e4/59be3494fbcb9f18.css\" id=\"all-css-8\" media=\"all\" rel=\"stylesheet\" type=\"text/css\"/>\n<link href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css?ver=11.4.0\" id=\"highlightjs-css-css\" media=\"all\" rel=\"stylesheet\"/>\n<link href=\"https://github.blog/wp-json/\" rel=\"https://api.w.org/\"/><link href=\"https://github.blog/wp-json/wp/v2/posts/96339\" rel=\"alternate\" title=\"JSON\" type=\"application/json\"/><link href=\"https://github.blog/xmlrpc.php?rsd\" rel=\"EditURI\" title=\"RSD\" type=\"application/rsd+xml\"/>\n<meta content=\"WordPress 6.9.4\" name=\"generator\"/>\n<link href=\"https://wp.me/pamS32-p3R\" rel=\"shortlink\"/>\n<!-- Stream WordPress user activity plugin v4.1.2 -->\n<style>img#wpstats{display:none}</style>\n<link href=\"https://github.blog/llms.txt\" rel=\"llms-sitemap\"/>\n<meta content=\"https://collector.githubapp.com/github-blog/collect\" name=\"ha-url\"/><link as=\"font\" crossorigin=\"anonymous\" href=\"https://github.blog/wp-content/themes/github-2021/dist/fonts/mona-sans.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"anonymous\" href=\"https://github.blog/wp-content/themes/github-2021/dist/fonts/MonaspaceNeon-Regular.woff2\" rel=\"preload\" type=\"font/woff2\"/><link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=32%2C32\" rel=\"icon\" sizes=\"32x32\"/>\n<link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=192%2C192\" rel=\"icon\" sizes=\"192x192\"/>\n<link href=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=180%2C180\" rel=\"apple-touch-icon\"/>\n<meta content=\"https://github.blog/wp-content/uploads/2019/01/cropped-github-favicon-512.png?fit=270%2C270\" name=\"msapplication-TileImage\"/>\n<style id=\"core-block-supports-inline-css\">\n.wp-container-core-buttons-is-layout-a89b3969{justify-content:center;}\n/*# sourceURL=core-block-supports-inline-css */\n</style>\n<style id=\"wp-style-engine-github-typography-presets-inline-css\">\n.is-typography-preset-h1{font-size:var(--base-size-36, 36px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:0;}.is-typography-preset-h2{font-size:var(--base-size-32, 32px);font-weight:700;line-height:1.25;margin-block-end:20px;margin-block-start:40px;}.is-typography-preset-h3{font-size:var(--base-size-28, 28px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h4{font-size:var(--base-size-24, 24px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h5{font-size:var(--base-size-20, 20px);font-weight:700;line-height:1.25;margin-block-end:12px;margin-block-start:20px;}.is-typography-preset-h6{font-size:var(--base-size-18, 18px);font-weight:700;line-height:1.25;margin-block-end:0;margin-block-start:20px;}.is-typography-preset-display{font:var(--text-display-shorthand);}.is-typography-preset-title-large{font:var(--text-title-shorthand-large);}.is-typography-preset-title-medium{font:var(--text-title-shorthand-medium);}.is-typography-preset-title-small{font:var(--text-title-shorthand-small);}.is-typography-preset-subtitle{font:var(--text-subtitle-shorthand);}.is-typography-preset-body-large{font:var(--text-body-shorthand-large);}.is-typography-preset-body-medium{font:var(--text-body-shorthand-medium);}.is-typography-preset-body-small{font:var(--text-body-shorthand-small);}\n/*# sourceURL=wp-style-engine-github-typography-presets-inline-css */\n</style>","truncated":true,"truncatedBy":"bytes","totalLines":220,"totalBytes":63722,"outputLines":118,"outputBytes":42787,"lastLinePartial":false,"firstLineExceedsLimit":false,"maxLines":2000,"maxBytes":51200}},"isError":false,"timestamp":1780219531844}}
{"type":"message","id":"317eaf1c","parentId":"048c4bbe","timestamp":"2026-05-31T09:25:31.845Z","message":{"role":"toolResult","toolCallId":"call_stXwYgiqOumhut6MNi1OylI1|fc_0f029265420fe376016a1bfe8bab30819182d85ef66f12cf72","toolName":"read","content":[{"type":"text","text":"<!doctype html>\n<!DOCTYPE html>\n\n<html data-scroll-behavior=\"smooth\" lang=\"en\" style=\"--progress-width: 0%;\"><head><base href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\"/><style type=\"text/css\">.truste_caIcon_display {display: block !important;}</style><style type=\"text/css\">.truste_cursor_pointer {cursor: pointer;}.truste_border_none {border: none;}.truste_accessible_link {color: #1D4ED8;font-size: 14px;font-weight: 600;text-decoration: underline;}.truste_accessible_link:hover {color: #1D4ED8;text-decoration: none !important;}.truste_accessible_link:focus-visible {outline: none;border-radius: 4px;box-shadow: 0 0 0 1px #FFFFFF, 0 0 0 4px #3699F1;}</style><style type=\"text/css\">.ta-display-none { display: none; }</style><style type=\"text/css\"> .truste_caIcon_display, a#teconsent {color: #4d5b7c;font-family: Inter, sans-serif;font-size: 14px;font-weight: 400;}</style><meta charset=\"utf-8\"/><meta content=\"width=device-width, initial-scale=1, minimum-scale=1\" name=\"viewport\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/636a5ac981f94f8b.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/bb3ef058b751a6ad-s.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F007BlogBanners2024%2Fdatabases-1%28kiwi%29.png&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F007BlogBanners2024%2Fdatabases-1%28kiwi%29.png&amp;width=828 2x\" rel=\"preload\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2F0-BLOG-BANNERS%2Fapp-platform-1%28tulip%29%2520%281%29.png&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2F0-BLOG-BANNERS%2Fapp-platform-1%28tulip%29%2520%281%29.png&amp;width=828 2x\" rel=\"preload\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2Fgradient_ai_platform_blog_headers%2Fdo-1400x815%2520%281%29.svg&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2Fgradient_ai_platform_blog_headers%2Fdo-1400x815%2520%281%29.svg&amp;width=828 2x\" rel=\"preload\"/><link data-precedence=\"next\" href=\"/rss-offline/assets/c22ede05fdcde865/97a79e4b4aaa8dd0.css\" rel=\"stylesheet\"/><link data-precedence=\"next\" href=\"/rss-offline/assets/c22ede05fdcde865/8048675823972ead.css\" rel=\"stylesheet\"/><link data-precedence=\"next\" href=\"/rss-offline/assets/c22ede05fdcde865/3b31c6a23419a4de.css\" rel=\"stylesheet\"/><link as=\"script\" fetchpriority=\"low\" href=\"/_next/static/chunks/webpack-5acd975349974fcb.js\" rel=\"preload\"/><script async=\"\" crossorigin=\"anonymous\" integrity=\"sha384-0QKRyflfD7HralXNtrlEj+djFxUCAlXdIRQwss7f2ldUa/xkc6Ra0akH4jxA0SXw\" src=\"https://cdn.amplitude.com/libs/analytics-browser-gtm-2.12.2-min.js.gz\" type=\"text/javascript\"></script><script async=\"\" src=\"https://cdn.amplitude.com/libs/analytics-browser-gtm-wrapper-3.15.0.js.br\" type=\"text/javascript\"></script><script async=\"\" src=\"https://www.googletagmanager.com/gtag/js?id=G-TYR2BYTLL0&amp;cx=c&amp;gtm=4e65r2\" type=\"text/javascript\"></script><script async=\"\" src=\"https://segment.digitalocean.com/next-integrations/actions/amplitude-plugins/f12a4347e1080fb88155.js\" status=\"loaded\" type=\"text/javascript\"></script><script async=\"\" src=\"https://www.googletagmanager.com/gtag/js?id=AW-1010666955&amp;cx=c&amp;gtm=4e65r2\" type=\"text/javascript\"></script><script async=\"\" src=\"https://www.googletagmanager.com/gtm.js?id=GTM-KHWBBT\"></script><script async=\"\" src=\"/_next/static/chunks/e37c2aa4-5cf4f0ffce7f33c3.js\"></script><script async=\"\" src=\"/_next/static/chunks/72977-15021371ea780c22.js\"></script><script async=\"\" src=\"/_next/static/chunks/main-app-e93c196b80424b47.js\"></script><script async=\"\" src=\"/_next/static/chunks/52774a7f-6e717d13cd568501.js\"></script><script async=\"\" src=\"/_next/static/chunks/51862-642fb5d1c8a5fcec.js\"></script><script async=\"\" src=\"/_next/static/chunks/10354-e8e9e835e41d743c.js\"></script><script async=\"\" src=\"/_next/static/chunks/58861-dc33c581fc04cfc4.js\"></script><script async=\"\" src=\"/_next/static/chunks/21123-c8c19e190616ba65.js\"></script><script async=\"\" src=\"/_next/static/chunks/54312-2744aafc4d1e872d.js\"></script><script async=\"\" src=\"/_next/static/chunks/55439-cacd0196b729047a.js\"></script><script async=\"\" src=\"/_next/static/chunks/46365-adb3d25453833cfa.js\"></script><script async=\"\" src=\"/_next/static/chunks/50041-71770735d5009bba.js\"></script><script async=\"\" src=\"/_next/static/chunks/6414-a57682b978c66cc2.js\"></script><script async=\"\" src=\"/_next/static/chunks/98500-1d18742db5ca4cc1.js\"></script><script async=\"\" src=\"/_next/static/chunks/34354-17f06a31062345f2.js\"></script><script async=\"\" src=\"/_next/static/chunks/5033-fe8e14aedb7941c6.js\"></script><script async=\"\" src=\"/_next/static/chunks/96431-a5bbc82a5a9adaf7.js\"></script><script async=\"\" src=\"/_next/static/chunks/77432-5cb1d10cd43ef858.js\"></script><script async=\"\" src=\"/_next/static/chunks/24296-dc05b08882b4536d.js\"></script><script async=\"\" src=\"/_next/static/chunks/app/layout-454c69dc74eea66a.js\"></script><script async=\"\" src=\"/_next/static/chunks/89728-3ea1b7df168e4a4a.js\"></script><script async=\"\" src=\"/_next/static/chunks/app/error-14d6846e0ca2dec5.js\"></script><script async=\"\" src=\"/_next/static/chunks/app/not-found-8f620335b8b462e9.js\"></script><script async=\"\" src=\"/_next/static/chunks/99613-d9b75c68fed770f8.js\"></script><script async=\"\" src=\"/_next/static/chunks/44753-5a3653741de24188.js\"></script><script async=\"\" src=\"/_next/static/chunks/73953-7b21915b5167a48a.js\"></script><script async=\"\" src=\"/_next/static/chunks/app/blog/%5Bslug%5D/page-5f94f030fefc32e7.js\"></script><meta content=\"\" name=\"next-size-adjust\"/><meta content=\"#0069ff\" name=\"theme-color\"/><title>OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean</title><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" name=\"description\"/><link href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\" rel=\"canonical\"/><meta content=\"OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean\" property=\"og:title\"/><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" property=\"og:description\"/><meta content=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\" property=\"og:url\"/><meta content=\"https://doimages.nyc3.cdn.digitaloceanspaces.com/002Blog/gradient_ai_platform_blog_headers/news-2(lavender).png\" property=\"og:image\"/><meta content=\"summary_large_image\" name=\"twitter:card\"/><meta content=\"OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean\" name=\"twitter:title\"/><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" name=\"twitter:description\"/><meta content=\"https://doimages.nyc3.cdn.digitaloceanspaces.com/002Blog/gradient_ai_platform_blog_headers/news-2(lavender).png\" name=\"twitter:image\"/><link href=\"/_next/static/media/favicon.594d6067.ico\" rel=\"icon\" type=\"image/x-icon\"/><link href=\"/_next/static/media/favicon-16x16.71c34517.png\" rel=\"icon\" sizes=\"16x16\" type=\"image/png\"/><link href=\"/_next/static/media/favicon-32x32.b7ef9ede.png\" rel=\"icon\" sizes=\"32x32\" type=\"image/png\"/><link href=\"/_next/static/media/android-chrome-192x192.f09059d8.png\" rel=\"icon\" sizes=\"192x192\"/><link href=\"/_next/static/media/android-chrome-512x512.5f2e6221.png\" rel=\"icon\" sizes=\"512x512\"/><link href=\"/_next/static/media/apple-touch-icon.d7edaa01.png\" rel=\"apple-touch-icon\" sizes=\"180x180\"/><script nomodule=\"\" src=\"/_next/static/chunks/polyfills-42372ed130431b0a.js\"></script><style data-styled=\"active\" data-styled-version=\"6.4.1\"></style><script async=\"\" src=\"https://googleads.g.doubleclick.net/pagead/viewthroughconversion/1010666955/?random=1780218094472&amp;cv=11&amp;fst=1780218094472&amp;bg=ffffff&amp;guid=ON&amp;async=1&amp;en=gtag.config&amp;gtm=45be65r2v899879189z86758737za20gzb6758737zd6758737xec&amp;gcd=13l3l3R3l5l1&amp;dma=0&amp;tag_exp=0~115938466~115938469~116701382~119034493&amp;u_w=800&amp;u_h=600&amp;url=https%3A%2F%2Fwww.digitalocean.com%2Fblog%2Fdigitalocean-opencode-inference-routers&amp;rcb=18&amp;frm=0&amp;tiba=OpenCode%20Now%20Supports%20DigitalOcean%20Inference%20Router%20for%20Intelligent%20Model%20Routing%20%7C%20DigitalOcean&amp;did=dNTIxZG&amp;gdid=dNTIxZG&amp;hn=www.googleadservices.com&amp;npa=0&amp;pscdl=noapi&amp;auid=982976011.1780218094&amp;uaa=arm&amp;uab=64&amp;uafvl=Chromium%3B147.0.7727.101%7CNot.A%252FBrand%3B8.0.0.0&amp;uamb=0&amp;uam=&amp;uap=Linux&amp;uapv=&amp;uaw=0&amp;_tu=CA&amp;data=event%3Dgtag.config&amp;rfmt=3&amp;fmt=4\" type=\"text/javascript\"></script><style id=\"ta-display-style\">/* */</style><script async=\"\" data-pocus-project-id=\"e0b704fa-d836-44f4-9283-b4b5ef8de34e\" defer=\"\" src=\"https://pocustrack.com/pocus.js\"></script><style>body{margin-top:72px!important}.rss-offline-banner{position:fixed;top:0;left:0;right:0;z-index:2147483647;background:#07111f;color:#fff;padding:12px 16px;font:14px/1.4 system-ui,sans-serif;box-shadow:0 1px 0 rgba(255,255,255,.08)}.rss-offline-banner a{color:#8dc3ff;text-decoration:none}.rss-offline-banner strong{color:#fff}.rss-offline-banner .muted{opacity:.78}</style></head><body><div class=\"rss-offline-banner\"><a href=\"/rss-offline/\">← rss-offline</a> · <a href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\">abrir original</a> · <strong>OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing [rendered]</strong> · <span class=\"muted\">DigitalOcean Blog · 2026-05-28 18:02 </span></div><div hidden=\"\"><!--$--><!--/$--></div><div class=\"NotificationsStyles__NotificationsContainer-sc-69d0afda-0 jTWtSQ\"></div><div class=\"Layout__StyledLayout-sc-9a47750a-0 bSJGQF\"><div class=\"HeaderStyles__StyledHeader-sc-c7d2c41b-0 dhpqCc\"><div aria-label=\"Top Bar\" class=\"TopBarstyles__StyledTopBar-sc-17n1n7p-0 iMEWEd\"><nav class=\"TopBarstyles__StyledTopBarContainer-sc-17n1n7p-1 dAVvrw\"><div class=\"TopBarstyles__StyledTopBarCTAs-sc-17n1n7p-2 dPzlrU\"><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/inference-router-architecture\" target=\"\">Inside the Architecture of DigitalOcean’s Inference Router<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 cjTPsg\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/introducing-digitalocean-ai-native-cloud\" target=\"\">Introducing DigitalOcean AI-Native Cloud<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/whats-new-on-inference-engine#week-of-may-4\" target=\"\">Now Available: Kimi K2.6<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.youtube.com/live/NwnW1zZZz7o\" target=\"_blank\">Missed the Deploy 2026 Keynote live? Watch it now and catch everything that was announced<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div></div><ul class=\"TopBarstyles__StyledTopBarLinks-sc-17n1n7p-3 finBGi\"><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/blog\">Blog</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"https://docs.digitalocean.com/products\">Docs</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/careers\">Careers</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/support\">Get Support</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/company/contact/sales?referrer=tophat\">Contact Sales</a></li></ul></nav></div><div class=\"Headerstyles__StyledBackdrop-sc-9ucsot-7 hUHGVR\"></div><header class=\"Headerstyles__StyledHeader-sc-9ucsot-0 hjBrds\"><div class=\"Headerstyles__StyledHeaderContainer-sc-9ucsot-1 fRyKcE\"><div class=\"Headerstyles__StyledHeaderLogo-sc-9ucsot-2 eALFRw\"><a href=\"/\"><span class=\"Typographystyles-sc-o7qsl9-0 jBwEfV\">DigitalOcean</span><div aria-hidden=\"true\" class=\"DigitalOceanStyles__StyledLogo-sc-5322d8c4-1 buQIZT\"><svg class=\"DigitalOceanSmileyStyles__StyledSvg-sc-64ca7b97-4 dYCpBs\" fill=\"none\" height=\"32\" tabindex=\"-1\" viewbox=\"0 0 90 90\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><circle class=\"DigitalOceanSmileyStyles__StyledCircle-sc-64ca7b97-0 bmFsHR\" cx=\"45\" cy=\"45\" fill=\"none\" r=\"36.25\" stroke=\"currentColor\" stroke-dasharray=\"170.824100538945 56.941366846315\" stroke-dashoffset=\"113.88273369263\" stroke-width=\"17.5\"></circle><rect class=\"DigitalOceanSmileyStyles__StyledPixelSm-sc-64ca7b97-1 yOpkM\" fill=\"currentColor\" height=\"11\" width=\"11\" x=\"3.25\" y=\"61.5\"></rect><rect class=\"DigitalOceanSmileyStyles__StyledPixelMd-sc-64ca7b97-2 kpiCiD\" fill=\"currentColor\" height=\"13.5\" width=\"13.5\" x=\"14.25\" y=\"72.5\"></rect><rect class=\"DigitalOceanSmileyStyles__StyledPixelLg-sc-64ca7b97-3 hLOPbg\" fill=\"currentColor\" height=\"17.25\" width=\"17.25\" x=\"27.5\" y=\"55.25\"></rect></svg><svg class=\"DigitalOceanStyles__StyledText-sc-5322d8c4-0 kGjwzS\" fill=\"currentColor\" height=\"32\" viewbox=\"32 0 159 32\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M57.242 9.305c-1.835-1.254-4.129-1.9-6.806-1.9h-5.812v18.399h5.812c2.677 0 4.971-.685 6.806-2.015.994-.722 1.797-1.711 2.333-2.965.535-1.255.841-2.699.841-4.334 0-1.635-.268-3.079-.841-4.296-.536-1.254-1.3-2.242-2.333-2.889Zm-9.213 1.179h1.835c2.027 0 3.709.38 4.971 1.178 1.415.837 2.141 2.471 2.141 4.79 0 2.395-.726 4.068-2.141 4.98-1.224.798-2.868 1.217-4.933 1.217h-1.835V10.484h-.038Zm16.479-3.346c-.574 0-1.032.191-1.415.571-.382.38-.612.836-.612 1.406s.192 1.027.574 1.445c.382.38.879.57 1.453.57.574 0 1.032-.19 1.453-.57.382-.38.574-.875.574-1.445 0-.57-.192-1.026-.574-1.406-.421-.342-.918-.571-1.453-.571Zm-1.683 5.664h3.288v12.963h-3.288zm15.177 1.103c-.994-.874-2.103-1.369-3.288-1.369-1.797 0-3.327.609-4.474 1.863-1.185 1.217-1.759 2.775-1.759 4.638 0 1.825.574 3.383 1.721 4.638 1.147 1.216 2.677 1.824 4.474 1.824 1.262 0 2.332-.342 3.25-1.026v.304c0 1.064-.306 1.901-.841 2.471-.574.57-1.377.874-2.333.874-1.529 0-2.447-.608-3.632-2.128l-2.218 2.128.076.076c.498.685 1.224 1.331 2.18 1.939.956.608 2.218.95 3.633.95 1.95 0 3.517-.608 4.664-1.786 1.148-1.179 1.759-2.775 1.759-4.714V12.803h-3.212v1.102Zm-.878 7.641c-.574.646-1.3.95-2.256.95-.956 0-1.683-.304-2.218-.95-.574-.646-.841-1.483-.841-2.547s.267-1.901.841-2.547c.574-.646 1.3-.95 2.218-.95.956 0 1.682.304 2.256.95.573.646.841 1.521.841 2.547.038 1.064-.268 1.901-.841 2.547Zm6.883-8.744h3.288v12.963h-3.288zm1.685-5.664c-.574 0-1.033.191-1.415.571-.382.38-.612.836-.612 1.406s.191 1.027.574 1.445c.382.38.879.57 1.453.57.573 0 1.032-.19 1.453-.57.382-.38.573-.875.573-1.445 0-.57-.191-1.026-.573-1.406-.421-.342-.88-.571-1.453-.571Zm8.793 2.167h-3.212v3.498h-1.874v2.965h1.874v5.36c0 1.672.344 2.889.994 3.573.689.684 1.874 1.064 3.518 1.064.535 0 1.071 0 1.606-.038h.153v-2.965l-1.109.076c-.765 0-1.3-.152-1.568-.418-.267-.266-.382-.836-.382-1.71v-4.904h3.059v-2.965h-3.059V9.305Zm18.43-1.939h3.288v18.4h-3.288v-18.4Zm36.364 13.762c-.574.646-1.186 1.216-1.645 1.52-.458.304-1.032.418-1.682.418-.956 0-1.759-.342-2.371-1.064-.612-.722-.956-1.635-.956-2.737s.306-2.015.918-2.737c.612-.722 1.415-1.064 2.371-1.064 1.07 0 2.179.646 3.135 1.786l2.18-2.053c-1.415-1.824-3.212-2.661-5.354-2.661-1.797 0-3.364.647-4.626 1.939-1.262 1.293-1.912 2.889-1.912 4.828 0 1.939.65 3.573 1.912 4.828 1.262 1.292 2.829 1.939 4.626 1.939 2.371 0 4.283-1.027 5.583-2.89l-2.179-2.052Zm13.459-6.501c-.459-.646-1.109-1.178-1.874-1.558-.765-.38-1.683-.57-2.715-.57-1.835 0-3.327.684-4.435 1.976-1.109 1.331-1.645 2.965-1.645 4.866 0 1.977.612 3.574 1.797 4.828 1.186 1.216 2.792 1.863 4.742 1.863 2.218 0 4.015-.875 5.391-2.623l.077-.076-2.141-2.053c-.192.228-.498.494-.727.76-.306.304-.612.532-.956.684a3.856 3.856 0 0 1-1.644.381c-.918 0-1.644-.266-2.218-.799-.535-.494-.841-1.14-.918-1.976h8.68l.039-1.179c0-.836-.115-1.635-.345-2.395-.305-.798-.65-1.482-1.108-2.129Zm-7.15 3.042a3.28 3.28 0 0 1 .841-1.521c.459-.418 1.032-.646 1.721-.646.803 0 1.414.228 1.835.684.382.418.612.912.65 1.521h-5.047v-.038Zm19.731-3.878c-.994-.836-2.37-1.255-4.091-1.255-1.109 0-2.103.229-3.021.685a5.297 5.297 0 0 0-2.218 2.09l.039.038 2.103 2.015c.879-1.368 1.835-1.862 3.097-1.862.688 0 1.262.19 1.682.532.421.342.65.798.65 1.406v.647c-.803-.229-1.606-.381-2.409-.381-1.644 0-2.944.381-3.938 1.141-.994.76-1.491 1.863-1.491 3.231 0 1.217.42 2.205 1.262 2.927.841.722 1.911 1.065 3.173 1.065s2.409-.495 3.48-1.369v1.064h3.212V17.44c-.039-1.596-.536-2.813-1.53-3.649Zm-5.813 7.032c.383-.266.88-.38 1.568-.38a7.31 7.31 0 0 1 2.485.457v1.254c-.726.646-1.682.988-2.829.988-.574 0-.994-.114-1.3-.38-.306-.228-.459-.532-.459-.95 0-.418.153-.76.535-.989Zm19.922-6.804c-.918-1.026-2.18-1.52-3.824-1.52-1.3 0-2.37.38-3.135 1.102v-.798h-3.212v12.963h3.288v-7.147c0-.988.23-1.749.689-2.319.458-.57 1.108-.836 1.95-.836.726 0 1.3.228 1.72.722.421.494.612 1.178.612 2.053v7.489h3.288v-7.489c0-1.787-.458-3.193-1.376-4.22Zm-80.64-.228c-.994-.836-2.371-1.255-4.091-1.255-1.109 0-2.103.229-3.021.685a5.291 5.291 0 0 0-2.218 2.09l.038.038 2.103 2.015c.88-1.368 1.836-1.862 3.098-1.862.688 0 1.261.19 1.682.532.421.342.65.798.65 1.406v.647c-.803-.229-1.606-.381-2.409-.381-1.644 0-2.944.381-3.938 1.141-.994.76-1.492 1.863-1.492 3.231 0 1.217.421 2.205 1.262 2.927s1.912 1.065 3.174 1.065 2.409-.495 3.48-1.369v1.064h3.211V17.44c-.038-1.596-.535-2.813-1.529-3.649Zm-5.813 7.032c.383-.266.88-.38 1.568-.38a7.31 7.31 0 0 1 2.485.457v1.254c-.726.646-1.682.988-2.829.988-.574 0-.994-.114-1.3-.38-.306-.228-.459-.532-.459-.95 0-.418.153-.76.535-.989Zm25.082 5.209c-5.238 0-9.521-4.258-9.521-9.466 0-5.208 4.283-9.466 9.521-9.466 5.239 0 9.521 4.258 9.521 9.466 0 5.208-4.244 9.466-9.521 9.466Zm-.001-15.624c-3.403 0-6.156 2.737-6.156 6.12s2.753 6.121 6.156 6.121c3.403 0 6.156-2.738 6.156-6.121 0-3.383-2.753-6.12-6.156-6.12Z\"></path></svg></div></a></div><nav aria-label=\"Main\" class=\"Navigationstyles__StyledNavigation-sc-16d688r-0 eEGMpp\" links=\"[object Object],[object Object],[object Object],[object Object],[object Object]\"><div class=\"Navigationstyles__StyledNavigationContainer-sc-16d688r-1 kwigWO\"><ul class=\"Navigationstyles__StyledNavigationList-sc-16d688r-2 fNSjwz\"><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Products</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Featured AI Products</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Compute</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Build, deploy, and scale cloud compute resources</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Containers and Images</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Safely store and manage containers and backups</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Managed Databases</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Fully managed resources running popular database engines</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Management and Dev Tools</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Control infrastructure and gather insights</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Networking</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Secure and control traffic to apps</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Security</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Help protect your account and resources with these security features</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Storage</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Store and access any amount of data reliably in the cloud</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Dropdownstyles__StyledButton-sc-16wobc6-10 ipGTcU\"><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 ghKDsS\" href=\"/products\">Browse all products</a></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Solutions</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">AI/ML</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">CMS</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Data and IoT</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Developer Tools</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Gaming and Media</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">GPU</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Hosting</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Security and Networking</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Startups and SMBs</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Web and App Platforms</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Dropdownstyles__StyledButton-sc-16wobc6-10 ipGTcU\"><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 ghKDsS\" href=\"/solutions\">See all solutions</a></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Developers</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Community</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Documentation</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Developer Tools</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Get Involved</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Utilities and Help</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Partners</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Become a Partner</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Marketplace</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 fHpLgU\"><a href=\"/pricing\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Pricing</span></a></li></ul><div class=\"Navigationstyles__StyledNavigationContent-sc-16d688r-4 eyrOGo\"><ul class=\"HeaderStyles__StyledCTALinks-sc-c7d2c41b-1 bUgInD\"><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 iWOgpY\" href=\"https://cloud.digitalocean.com/login\">Log in</a></li><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 bUvsjy\" href=\"https://cloud.digitalocean.com/registrations/new\">Sign up</a></li></ul></div></div><div class=\"Navigationstyles__StyledNavigationBackground-sc-16d688r-5 tRHjk\" style=\"height:0px\"></div></nav><div class=\"Headerstyles__StyledHeaderContent-sc-9ucsot-4 dkWskY\"><ul class=\"HeaderStyles__StyledCTALinks-sc-c7d2c41b-1 bUgInD\"><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 iWOgpY\" href=\"https://cloud.digitalocean.com/login\">Log in</a></li><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 bUvsjy\" href=\"https://cloud.digitalocean.com/registrations/new\">Sign up</a></li></ul></div><div class=\"Headerstyles__StyledDivider-sc-9ucsot-6 ihiyeA\"></div><button aria-label=\"Menu\" class=\"Headerstyles__StyledHeaderToggle-sc-9ucsot-3 dKbgtA\" type=\"button\"><svg fill=\"none\" height=\"32\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><line x1=\"3\" x2=\"21\" y1=\"12\" y2=\"12\"></line><line x1=\"3\" x2=\"21\" y1=\"6\" y2=\"6\"></line><line x1=\"3\" x2=\"21\" y1=\"18\" y2=\"18\"></line></svg></button></div></header><div class=\"Headerstyles__StyledBackdrop-sc-9ucsot-7 hUHGVR\"></div><div class=\"HeaderStyles__StyledProgressBar-sc-c7d2c41b-4 kcLFAK\"></div></div><!--$--><div class=\"Sectionstyles__StyledSection-sc-4l5hhw-0 ewQfcU blog-slug__StyledHeroSection-sc-3613c11f-4 hxLxqI\"><div class=\"Sectionstyles__StyledSectionInner-sc-4l5hhw-1 jQSBPu\"><div class=\"Containerstyles-sc-11hjsrs-0 dyboab blog-slug__StyledContainer-sc-3613c11f-0 hFxgiN\"><div class=\"Categorystyles__StyledCategory-sc-1lstx5-0 dMFUvU\" style=\"align-self:center\"><a href=\"/blog/tags/product-updates\">Product updates</a></div><div class=\"Containerstyles-sc-11hjsrs-0 hBhvxC\"><div class=\"Headlinestyles__StyledHeadline-sc-14uj3bo-1 hNsSJd\"><div class=\"Headlinestyles__StyledHeadlineHeader-sc-14uj3bo-2 bZmvnY\"><h1 class=\"Typographystyles-sc-o7qsl9-0 etpTBk\">OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing</h1></div></div></div><div class=\"blog-slug__StyledAuthorDetails-sc-3613c11f-1 gHtxhU\"><div><div class=\"AuthorsStyles__StyledAuthorList-sc-6bfa00a2-0 gGBXKw\"><div class=\"AuthorsStyles__StyledAvatarGroup-sc-6bfa00a2-2 fhNrjq\"><img alt=\"author\" class=\"AuthorsStyles__StyledAvatarSmall-sc-6bfa00a2-1 hBOddK\" data-nimg=\"1\" decoding=\"async\" height=\"48\" loading=\"lazy\" src=\"https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro\" srcset=\"https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro 1x, https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro 2x\" style=\"color:transparent\" width=\"48\"/></div><div class=\"AuthorsStyles__StyledAuthors-sc-6bfa00a2-4 fWmRpd\"><div class=\"AuthorsStyles__StyledAuthor-sc-6bfa00a2-3 bGdJmG\"><p class=\"Typographystyles-sc-o7qsl9-0 kZPABH\">By<!-- --> <a class=\"LinkInlinestyles-sc-18du0ds-0 eSWPpp\" href=\"/community/users/musamalik\">Musa Malik</a></p></div><p class=\"Typographystyles-sc-o7qsl9-0 dLrRIe\">AI/ML Engineer</p></div></div></div></div><div class=\"blog-slug__StyledMetaContainer-sc-3613c11f-2 ircmKC\"><ul class=\"blog-slug__StyledMetaInfo-sc-3613c11f-3 dxHuIJ\"><li class=\"Typographystyles-sc-o7qsl9-0 hzYaek\"><span class=\"Typographystyles-sc-o7qsl9-0 cOddxv\">Updated:<!-- --> <time datetime=\"2026-05-28T22:29:54.234Z\" itemprop=\"dateModified\">May 28, 2026</time></span></li><li class=\"Typographystyles-sc-o7qsl9-0 hzYaek\">3 min read</li></ul></div></div></div></div><div class=\"Sidebar__StyledSidebar-sc-b4087769-0 hAMSBs\"><div class=\"Sidebar__StyledSidebarContainer-sc-b4087769-7 ifJHgy\"><div class=\"Sidebar__StyledSidebarLeftSidebar-sc-b4087769-1 bQcpjB\"><div class=\"Sidebar__StyledSidebarLeftSidebarContainer-sc-b4087769-3 iqqBqJ\"><a class=\"Typographystyles-sc-o7qsl9-0 kZPABH Sidebar__StyledSidebarLeftSidebarLink-sc-b4087769-6 cJMIga\" href=\"/blog\">&lt;-<!-- --> <!-- -->Back to blog home</a><svg height=\"1\" preserveaspectratio=\"none\" viewbox=\"0 0 100 1\" width=\"100%\" xmlns=\"http://www.w3.org/2000/svg\"><line stroke=\"#8A96B5\" stroke-dasharray=\"4 8\" stroke-linecap=\"round\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" x1=\"0\" x2=\"100\" y1=\"0.5\" y2=\"0.5\"></line></svg><div class=\"TableOfContents__StyledTOC-sc-b456a232-0 izKMTV\"><nav aria-labelledby=\"_r_1_\" class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigation-sc-og8kyu-0 coZErM TableOfContents__StyledLargeSideNavigation-sc-b456a232-1 gnBxmL\"><h2 class=\"Typographystyles-sc-o7qsl9-0 KrLKk\" id=\"_r_1_\">Table of contents</h2><ol class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationContainer-sc-og8kyu-1 lfwyaB\"><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#what-is-an-inference-router\">What is an Inference Router?</a></span></li><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#what-changed-for-opencode\">What Changed for OpenCode</a></span></li><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#beyond-coding-agents\">Beyond Coding Agents</a></span></li></ol></nav><div class=\"TableOfContents__StyledSmallSideNavigation-sc-b456a232-2 ejWwSl\"><button class=\"TableOfContents__StyledTocButton-sc-b456a232-3 fVTOSb\">Table of contents<div class=\"TableOfContents__StyledTabbedSideNavigationIcon-sc-b456a232-6 cxOPrG\"><svg fill=\"none\" height=\"32\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><line x1=\"5\" x2=\"19\" y1=\"12\" y2=\"12\"></line><polyline points=\"12 5 19 12 12 19\"></polyline></svg></div></button><div class=\"TableOfContents__StyledTocDropdown-sc-b456a232-4 dwfzgi\"><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#what-is-an-inference-router\">What is an Inference Router?</a><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#what-changed-for-opencode\">What Changed for OpenCode</a><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#beyond-coding-agents\">Beyond Coding Agents</a></div></div></div></div></div><div class=\"Sidebar__StyledSidebarContent-sc-b4087769-5 hMcMnb\"><div class=\"blog-slug__StyledPostContent-sc-3613c11f-6 fiCMQz\"><div class=\"blog-slug__StyledPost-sc-3613c11f-5 dlNxMz\"><div class=\"Markdownstyles-sc-dd1icp-0 iceGPj MarkdownStyles__StyledMarkdown-sc-ee165f5-0 cWQrkY\"><p>Coding agents today have a massive spending problem. Every request, whether you’re designing system architecture or writing a single-line docstring, often gets routed to the same expensive frontier model. The result: unnecessary token usage, higher inference costs, and little awareness of task complexity or budget constraints.</p>\n<p>This high cost stems from a “one-size-fits-all” approach to model usage, where premium frontier models are utilized for trivial tasks that don’t require such intensive reasoning effort. In multi-agent workflows, where orchestrators delegate work to specialized subagents, this lack of discrimination frequently leads to runaway costs and opaque failure modes. Without intelligent routing, developers can essentially be forced into closed-provider lock-in and high API usage fees, which quickly escalate during exploratory building phases.</p>\n<p><a href=\"https://docs.digitalocean.com/products/inference/how-to/use-inference-router/\" rel=\"ugc nofollow noopener\" target=\"_blank\">DigitalOcean Inference Router</a>, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through <a href=\"https://opencode.ai/docs/providers/#digitalocean\" rel=\"ugc nofollow noopener\" target=\"_blank\">OpenCode</a>, the open-source AI coding agent, in as little as a few seconds.</p>\n<h2 id=\"what-is-an-inference-router\"><a href=\"#what-is-an-inference-router\" onclick=\"navigator.clipboard.writeText(this.href);\">What is an Inference Router?</a><a aria-hidden=\"true\" class=\"hash-anchor\" href=\"#what-is-an-inference-router\" onclick=\"navigator.clipboard.writeText(this.href);\"></a></h2>\n\n[Showing lines 1-7 of 47 (50.0KB limit). Use offset=8 to continue.]"}],"details":{"truncation":{"content":"<!doctype html>\n<!DOCTYPE html>\n\n<html data-scroll-behavior=\"smooth\" lang=\"en\" style=\"--progress-width: 0%;\"><head><base href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\"/><style type=\"text/css\">.truste_caIcon_display {display: block !important;}</style><style type=\"text/css\">.truste_cursor_pointer {cursor: pointer;}.truste_border_none {border: none;}.truste_accessible_link {color: #1D4ED8;font-size: 14px;font-weight: 600;text-decoration: underline;}.truste_accessible_link:hover {color: #1D4ED8;text-decoration: none !important;}.truste_accessible_link:focus-visible {outline: none;border-radius: 4px;box-shadow: 0 0 0 1px #FFFFFF, 0 0 0 4px #3699F1;}</style><style type=\"text/css\">.ta-display-none { display: none; }</style><style type=\"text/css\"> .truste_caIcon_display, a#teconsent {color: #4d5b7c;font-family: Inter, sans-serif;font-size: 14px;font-weight: 400;}</style><meta charset=\"utf-8\"/><meta content=\"width=device-width, initial-scale=1, minimum-scale=1\" name=\"viewport\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/636a5ac981f94f8b.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/bb3ef058b751a6ad-s.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F007BlogBanners2024%2Fdatabases-1%28kiwi%29.png&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F007BlogBanners2024%2Fdatabases-1%28kiwi%29.png&amp;width=828 2x\" rel=\"preload\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2F0-BLOG-BANNERS%2Fapp-platform-1%28tulip%29%2520%281%29.png&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2F0-BLOG-BANNERS%2Fapp-platform-1%28tulip%29%2520%281%29.png&amp;width=828 2x\" rel=\"preload\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2Fgradient_ai_platform_blog_headers%2Fdo-1400x815%2520%281%29.svg&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2Fgradient_ai_platform_blog_headers%2Fdo-1400x815%2520%281%29.svg&amp;width=828 2x\" rel=\"preload\"/><link data-precedence=\"next\" href=\"/rss-offline/assets/c22ede05fdcde865/97a79e4b4aaa8dd0.css\" rel=\"stylesheet\"/><link data-precedence=\"next\" href=\"/rss-offline/assets/c22ede05fdcde865/8048675823972ead.css\" rel=\"stylesheet\"/><link data-precedence=\"next\" href=\"/rss-offline/assets/c22ede05fdcde865/3b31c6a23419a4de.css\" rel=\"stylesheet\"/><link as=\"script\" fetchpriority=\"low\" href=\"/_next/static/chunks/webpack-5acd975349974fcb.js\" rel=\"preload\"/><script async=\"\" crossorigin=\"anonymous\" integrity=\"sha384-0QKRyflfD7HralXNtrlEj+djFxUCAlXdIRQwss7f2ldUa/xkc6Ra0akH4jxA0SXw\" src=\"https://cdn.amplitude.com/libs/analytics-browser-gtm-2.12.2-min.js.gz\" type=\"text/javascript\"></script><script async=\"\" src=\"https://cdn.amplitude.com/libs/analytics-browser-gtm-wrapper-3.15.0.js.br\" type=\"text/javascript\"></script><script async=\"\" src=\"https://www.googletagmanager.com/gtag/js?id=G-TYR2BYTLL0&amp;cx=c&amp;gtm=4e65r2\" type=\"text/javascript\"></script><script async=\"\" src=\"https://segment.digitalocean.com/next-integrations/actions/amplitude-plugins/f12a4347e1080fb88155.js\" status=\"loaded\" type=\"text/javascript\"></script><script async=\"\" src=\"https://www.googletagmanager.com/gtag/js?id=AW-1010666955&amp;cx=c&amp;gtm=4e65r2\" type=\"text/javascript\"></script><script async=\"\" src=\"https://www.googletagmanager.com/gtm.js?id=GTM-KHWBBT\"></script><script async=\"\" src=\"/_next/static/chunks/e37c2aa4-5cf4f0ffce7f33c3.js\"></script><script async=\"\" src=\"/_next/static/chunks/72977-15021371ea780c22.js\"></script><script async=\"\" src=\"/_next/static/chunks/main-app-e93c196b80424b47.js\"></script><script async=\"\" src=\"/_next/static/chunks/52774a7f-6e717d13cd568501.js\"></script><script async=\"\" src=\"/_next/static/chunks/51862-642fb5d1c8a5fcec.js\"></script><script async=\"\" src=\"/_next/static/chunks/10354-e8e9e835e41d743c.js\"></script><script async=\"\" src=\"/_next/static/chunks/58861-dc33c581fc04cfc4.js\"></script><script async=\"\" src=\"/_next/static/chunks/21123-c8c19e190616ba65.js\"></script><script async=\"\" src=\"/_next/static/chunks/54312-2744aafc4d1e872d.js\"></script><script async=\"\" src=\"/_next/static/chunks/55439-cacd0196b729047a.js\"></script><script async=\"\" src=\"/_next/static/chunks/46365-adb3d25453833cfa.js\"></script><script async=\"\" src=\"/_next/static/chunks/50041-71770735d5009bba.js\"></script><script async=\"\" src=\"/_next/static/chunks/6414-a57682b978c66cc2.js\"></script><script async=\"\" src=\"/_next/static/chunks/98500-1d18742db5ca4cc1.js\"></script><script async=\"\" src=\"/_next/static/chunks/34354-17f06a31062345f2.js\"></script><script async=\"\" src=\"/_next/static/chunks/5033-fe8e14aedb7941c6.js\"></script><script async=\"\" src=\"/_next/static/chunks/96431-a5bbc82a5a9adaf7.js\"></script><script async=\"\" src=\"/_next/static/chunks/77432-5cb1d10cd43ef858.js\"></script><script async=\"\" src=\"/_next/static/chunks/24296-dc05b08882b4536d.js\"></script><script async=\"\" src=\"/_next/static/chunks/app/layout-454c69dc74eea66a.js\"></script><script async=\"\" src=\"/_next/static/chunks/89728-3ea1b7df168e4a4a.js\"></script><script async=\"\" src=\"/_next/static/chunks/app/error-14d6846e0ca2dec5.js\"></script><script async=\"\" src=\"/_next/static/chunks/app/not-found-8f620335b8b462e9.js\"></script><script async=\"\" src=\"/_next/static/chunks/99613-d9b75c68fed770f8.js\"></script><script async=\"\" src=\"/_next/static/chunks/44753-5a3653741de24188.js\"></script><script async=\"\" src=\"/_next/static/chunks/73953-7b21915b5167a48a.js\"></script><script async=\"\" src=\"/_next/static/chunks/app/blog/%5Bslug%5D/page-5f94f030fefc32e7.js\"></script><meta content=\"\" name=\"next-size-adjust\"/><meta content=\"#0069ff\" name=\"theme-color\"/><title>OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean</title><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" name=\"description\"/><link href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\" rel=\"canonical\"/><meta content=\"OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean\" property=\"og:title\"/><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" property=\"og:description\"/><meta content=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\" property=\"og:url\"/><meta content=\"https://doimages.nyc3.cdn.digitaloceanspaces.com/002Blog/gradient_ai_platform_blog_headers/news-2(lavender).png\" property=\"og:image\"/><meta content=\"summary_large_image\" name=\"twitter:card\"/><meta content=\"OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean\" name=\"twitter:title\"/><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" name=\"twitter:description\"/><meta content=\"https://doimages.nyc3.cdn.digitaloceanspaces.com/002Blog/gradient_ai_platform_blog_headers/news-2(lavender).png\" name=\"twitter:image\"/><link href=\"/_next/static/media/favicon.594d6067.ico\" rel=\"icon\" type=\"image/x-icon\"/><link href=\"/_next/static/media/favicon-16x16.71c34517.png\" rel=\"icon\" sizes=\"16x16\" type=\"image/png\"/><link href=\"/_next/static/media/favicon-32x32.b7ef9ede.png\" rel=\"icon\" sizes=\"32x32\" type=\"image/png\"/><link href=\"/_next/static/media/android-chrome-192x192.f09059d8.png\" rel=\"icon\" sizes=\"192x192\"/><link href=\"/_next/static/media/android-chrome-512x512.5f2e6221.png\" rel=\"icon\" sizes=\"512x512\"/><link href=\"/_next/static/media/apple-touch-icon.d7edaa01.png\" rel=\"apple-touch-icon\" sizes=\"180x180\"/><script nomodule=\"\" src=\"/_next/static/chunks/polyfills-42372ed130431b0a.js\"></script><style data-styled=\"active\" data-styled-version=\"6.4.1\"></style><script async=\"\" src=\"https://googleads.g.doubleclick.net/pagead/viewthroughconversion/1010666955/?random=1780218094472&amp;cv=11&amp;fst=1780218094472&amp;bg=ffffff&amp;guid=ON&amp;async=1&amp;en=gtag.config&amp;gtm=45be65r2v899879189z86758737za20gzb6758737zd6758737xec&amp;gcd=13l3l3R3l5l1&amp;dma=0&amp;tag_exp=0~115938466~115938469~116701382~119034493&amp;u_w=800&amp;u_h=600&amp;url=https%3A%2F%2Fwww.digitalocean.com%2Fblog%2Fdigitalocean-opencode-inference-routers&amp;rcb=18&amp;frm=0&amp;tiba=OpenCode%20Now%20Supports%20DigitalOcean%20Inference%20Router%20for%20Intelligent%20Model%20Routing%20%7C%20DigitalOcean&amp;did=dNTIxZG&amp;gdid=dNTIxZG&amp;hn=www.googleadservices.com&amp;npa=0&amp;pscdl=noapi&amp;auid=982976011.1780218094&amp;uaa=arm&amp;uab=64&amp;uafvl=Chromium%3B147.0.7727.101%7CNot.A%252FBrand%3B8.0.0.0&amp;uamb=0&amp;uam=&amp;uap=Linux&amp;uapv=&amp;uaw=0&amp;_tu=CA&amp;data=event%3Dgtag.config&amp;rfmt=3&amp;fmt=4\" type=\"text/javascript\"></script><style id=\"ta-display-style\">/* */</style><script async=\"\" data-pocus-project-id=\"e0b704fa-d836-44f4-9283-b4b5ef8de34e\" defer=\"\" src=\"https://pocustrack.com/pocus.js\"></script><style>body{margin-top:72px!important}.rss-offline-banner{position:fixed;top:0;left:0;right:0;z-index:2147483647;background:#07111f;color:#fff;padding:12px 16px;font:14px/1.4 system-ui,sans-serif;box-shadow:0 1px 0 rgba(255,255,255,.08)}.rss-offline-banner a{color:#8dc3ff;text-decoration:none}.rss-offline-banner strong{color:#fff}.rss-offline-banner .muted{opacity:.78}</style></head><body><div class=\"rss-offline-banner\"><a href=\"/rss-offline/\">← rss-offline</a> · <a href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\">abrir original</a> · <strong>OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing [rendered]</strong> · <span class=\"muted\">DigitalOcean Blog · 2026-05-28 18:02 </span></div><div hidden=\"\"><!--$--><!--/$--></div><div class=\"NotificationsStyles__NotificationsContainer-sc-69d0afda-0 jTWtSQ\"></div><div class=\"Layout__StyledLayout-sc-9a47750a-0 bSJGQF\"><div class=\"HeaderStyles__StyledHeader-sc-c7d2c41b-0 dhpqCc\"><div aria-label=\"Top Bar\" class=\"TopBarstyles__StyledTopBar-sc-17n1n7p-0 iMEWEd\"><nav class=\"TopBarstyles__StyledTopBarContainer-sc-17n1n7p-1 dAVvrw\"><div class=\"TopBarstyles__StyledTopBarCTAs-sc-17n1n7p-2 dPzlrU\"><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/inference-router-architecture\" target=\"\">Inside the Architecture of DigitalOcean’s Inference Router<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 cjTPsg\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/introducing-digitalocean-ai-native-cloud\" target=\"\">Introducing DigitalOcean AI-Native Cloud<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/whats-new-on-inference-engine#week-of-may-4\" target=\"\">Now Available: Kimi K2.6<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.youtube.com/live/NwnW1zZZz7o\" target=\"_blank\">Missed the Deploy 2026 Keynote live? Watch it now and catch everything that was announced<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div></div><ul class=\"TopBarstyles__StyledTopBarLinks-sc-17n1n7p-3 finBGi\"><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/blog\">Blog</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"https://docs.digitalocean.com/products\">Docs</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/careers\">Careers</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/support\">Get Support</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/company/contact/sales?referrer=tophat\">Contact Sales</a></li></ul></nav></div><div class=\"Headerstyles__StyledBackdrop-sc-9ucsot-7 hUHGVR\"></div><header class=\"Headerstyles__StyledHeader-sc-9ucsot-0 hjBrds\"><div class=\"Headerstyles__StyledHeaderContainer-sc-9ucsot-1 fRyKcE\"><div class=\"Headerstyles__StyledHeaderLogo-sc-9ucsot-2 eALFRw\"><a href=\"/\"><span class=\"Typographystyles-sc-o7qsl9-0 jBwEfV\">DigitalOcean</span><div aria-hidden=\"true\" class=\"DigitalOceanStyles__StyledLogo-sc-5322d8c4-1 buQIZT\"><svg class=\"DigitalOceanSmileyStyles__StyledSvg-sc-64ca7b97-4 dYCpBs\" fill=\"none\" height=\"32\" tabindex=\"-1\" viewbox=\"0 0 90 90\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><circle class=\"DigitalOceanSmileyStyles__StyledCircle-sc-64ca7b97-0 bmFsHR\" cx=\"45\" cy=\"45\" fill=\"none\" r=\"36.25\" stroke=\"currentColor\" stroke-dasharray=\"170.824100538945 56.941366846315\" stroke-dashoffset=\"113.88273369263\" stroke-width=\"17.5\"></circle><rect class=\"DigitalOceanSmileyStyles__StyledPixelSm-sc-64ca7b97-1 yOpkM\" fill=\"currentColor\" height=\"11\" width=\"11\" x=\"3.25\" y=\"61.5\"></rect><rect class=\"DigitalOceanSmileyStyles__StyledPixelMd-sc-64ca7b97-2 kpiCiD\" fill=\"currentColor\" height=\"13.5\" width=\"13.5\" x=\"14.25\" y=\"72.5\"></rect><rect class=\"DigitalOceanSmileyStyles__StyledPixelLg-sc-64ca7b97-3 hLOPbg\" fill=\"currentColor\" height=\"17.25\" width=\"17.25\" x=\"27.5\" y=\"55.25\"></rect></svg><svg class=\"DigitalOceanStyles__StyledText-sc-5322d8c4-0 kGjwzS\" fill=\"currentColor\" height=\"32\" viewbox=\"32 0 159 32\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M57.242 9.305c-1.835-1.254-4.129-1.9-6.806-1.9h-5.812v18.399h5.812c2.677 0 4.971-.685 6.806-2.015.994-.722 1.797-1.711 2.333-2.965.535-1.255.841-2.699.841-4.334 0-1.635-.268-3.079-.841-4.296-.536-1.254-1.3-2.242-2.333-2.889Zm-9.213 1.179h1.835c2.027 0 3.709.38 4.971 1.178 1.415.837 2.141 2.471 2.141 4.79 0 2.395-.726 4.068-2.141 4.98-1.224.798-2.868 1.217-4.933 1.217h-1.835V10.484h-.038Zm16.479-3.346c-.574 0-1.032.191-1.415.571-.382.38-.612.836-.612 1.406s.192 1.027.574 1.445c.382.38.879.57 1.453.57.574 0 1.032-.19 1.453-.57.382-.38.574-.875.574-1.445 0-.57-.192-1.026-.574-1.406-.421-.342-.918-.571-1.453-.571Zm-1.683 5.664h3.288v12.963h-3.288zm15.177 1.103c-.994-.874-2.103-1.369-3.288-1.369-1.797 0-3.327.609-4.474 1.863-1.185 1.217-1.759 2.775-1.759 4.638 0 1.825.574 3.383 1.721 4.638 1.147 1.216 2.677 1.824 4.474 1.824 1.262 0 2.332-.342 3.25-1.026v.304c0 1.064-.306 1.901-.841 2.471-.574.57-1.377.874-2.333.874-1.529 0-2.447-.608-3.632-2.128l-2.218 2.128.076.076c.498.685 1.224 1.331 2.18 1.939.956.608 2.218.95 3.633.95 1.95 0 3.517-.608 4.664-1.786 1.148-1.179 1.759-2.775 1.759-4.714V12.803h-3.212v1.102Zm-.878 7.641c-.574.646-1.3.95-2.256.95-.956 0-1.683-.304-2.218-.95-.574-.646-.841-1.483-.841-2.547s.267-1.901.841-2.547c.574-.646 1.3-.95 2.218-.95.956 0 1.682.304 2.256.95.573.646.841 1.521.841 2.547.038 1.064-.268 1.901-.841 2.547Zm6.883-8.744h3.288v12.963h-3.288zm1.685-5.664c-.574 0-1.033.191-1.415.571-.382.38-.612.836-.612 1.406s.191 1.027.574 1.445c.382.38.879.57 1.453.57.573 0 1.032-.19 1.453-.57.382-.38.573-.875.573-1.445 0-.57-.191-1.026-.573-1.406-.421-.342-.88-.571-1.453-.571Zm8.793 2.167h-3.212v3.498h-1.874v2.965h1.874v5.36c0 1.672.344 2.889.994 3.573.689.684 1.874 1.064 3.518 1.064.535 0 1.071 0 1.606-.038h.153v-2.965l-1.109.076c-.765 0-1.3-.152-1.568-.418-.267-.266-.382-.836-.382-1.71v-4.904h3.059v-2.965h-3.059V9.305Zm18.43-1.939h3.288v18.4h-3.288v-18.4Zm36.364 13.762c-.574.646-1.186 1.216-1.645 1.52-.458.304-1.032.418-1.682.418-.956 0-1.759-.342-2.371-1.064-.612-.722-.956-1.635-.956-2.737s.306-2.015.918-2.737c.612-.722 1.415-1.064 2.371-1.064 1.07 0 2.179.646 3.135 1.786l2.18-2.053c-1.415-1.824-3.212-2.661-5.354-2.661-1.797 0-3.364.647-4.626 1.939-1.262 1.293-1.912 2.889-1.912 4.828 0 1.939.65 3.573 1.912 4.828 1.262 1.292 2.829 1.939 4.626 1.939 2.371 0 4.283-1.027 5.583-2.89l-2.179-2.052Zm13.459-6.501c-.459-.646-1.109-1.178-1.874-1.558-.765-.38-1.683-.57-2.715-.57-1.835 0-3.327.684-4.435 1.976-1.109 1.331-1.645 2.965-1.645 4.866 0 1.977.612 3.574 1.797 4.828 1.186 1.216 2.792 1.863 4.742 1.863 2.218 0 4.015-.875 5.391-2.623l.077-.076-2.141-2.053c-.192.228-.498.494-.727.76-.306.304-.612.532-.956.684a3.856 3.856 0 0 1-1.644.381c-.918 0-1.644-.266-2.218-.799-.535-.494-.841-1.14-.918-1.976h8.68l.039-1.179c0-.836-.115-1.635-.345-2.395-.305-.798-.65-1.482-1.108-2.129Zm-7.15 3.042a3.28 3.28 0 0 1 .841-1.521c.459-.418 1.032-.646 1.721-.646.803 0 1.414.228 1.835.684.382.418.612.912.65 1.521h-5.047v-.038Zm19.731-3.878c-.994-.836-2.37-1.255-4.091-1.255-1.109 0-2.103.229-3.021.685a5.297 5.297 0 0 0-2.218 2.09l.039.038 2.103 2.015c.879-1.368 1.835-1.862 3.097-1.862.688 0 1.262.19 1.682.532.421.342.65.798.65 1.406v.647c-.803-.229-1.606-.381-2.409-.381-1.644 0-2.944.381-3.938 1.141-.994.76-1.491 1.863-1.491 3.231 0 1.217.42 2.205 1.262 2.927.841.722 1.911 1.065 3.173 1.065s2.409-.495 3.48-1.369v1.064h3.212V17.44c-.039-1.596-.536-2.813-1.53-3.649Zm-5.813 7.032c.383-.266.88-.38 1.568-.38a7.31 7.31 0 0 1 2.485.457v1.254c-.726.646-1.682.988-2.829.988-.574 0-.994-.114-1.3-.38-.306-.228-.459-.532-.459-.95 0-.418.153-.76.535-.989Zm19.922-6.804c-.918-1.026-2.18-1.52-3.824-1.52-1.3 0-2.37.38-3.135 1.102v-.798h-3.212v12.963h3.288v-7.147c0-.988.23-1.749.689-2.319.458-.57 1.108-.836 1.95-.836.726 0 1.3.228 1.72.722.421.494.612 1.178.612 2.053v7.489h3.288v-7.489c0-1.787-.458-3.193-1.376-4.22Zm-80.64-.228c-.994-.836-2.371-1.255-4.091-1.255-1.109 0-2.103.229-3.021.685a5.291 5.291 0 0 0-2.218 2.09l.038.038 2.103 2.015c.88-1.368 1.836-1.862 3.098-1.862.688 0 1.261.19 1.682.532.421.342.65.798.65 1.406v.647c-.803-.229-1.606-.381-2.409-.381-1.644 0-2.944.381-3.938 1.141-.994.76-1.492 1.863-1.492 3.231 0 1.217.421 2.205 1.262 2.927s1.912 1.065 3.174 1.065 2.409-.495 3.48-1.369v1.064h3.211V17.44c-.038-1.596-.535-2.813-1.529-3.649Zm-5.813 7.032c.383-.266.88-.38 1.568-.38a7.31 7.31 0 0 1 2.485.457v1.254c-.726.646-1.682.988-2.829.988-.574 0-.994-.114-1.3-.38-.306-.228-.459-.532-.459-.95 0-.418.153-.76.535-.989Zm25.082 5.209c-5.238 0-9.521-4.258-9.521-9.466 0-5.208 4.283-9.466 9.521-9.466 5.239 0 9.521 4.258 9.521 9.466 0 5.208-4.244 9.466-9.521 9.466Zm-.001-15.624c-3.403 0-6.156 2.737-6.156 6.12s2.753 6.121 6.156 6.121c3.403 0 6.156-2.738 6.156-6.121 0-3.383-2.753-6.12-6.156-6.12Z\"></path></svg></div></a></div><nav aria-label=\"Main\" class=\"Navigationstyles__StyledNavigation-sc-16d688r-0 eEGMpp\" links=\"[object Object],[object Object],[object Object],[object Object],[object Object]\"><div class=\"Navigationstyles__StyledNavigationContainer-sc-16d688r-1 kwigWO\"><ul class=\"Navigationstyles__StyledNavigationList-sc-16d688r-2 fNSjwz\"><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Products</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Featured AI Products</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Compute</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Build, deploy, and scale cloud compute resources</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Containers and Images</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Safely store and manage containers and backups</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Managed Databases</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Fully managed resources running popular database engines</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Management and Dev Tools</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Control infrastructure and gather insights</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Networking</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Secure and control traffic to apps</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Security</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Help protect your account and resources with these security features</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Storage</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Store and access any amount of data reliably in the cloud</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Dropdownstyles__StyledButton-sc-16wobc6-10 ipGTcU\"><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 ghKDsS\" href=\"/products\">Browse all products</a></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Solutions</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">AI/ML</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">CMS</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Data and IoT</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Developer Tools</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Gaming and Media</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">GPU</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Hosting</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Security and Networking</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Startups and SMBs</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Web and App Platforms</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Dropdownstyles__StyledButton-sc-16wobc6-10 ipGTcU\"><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 ghKDsS\" href=\"/solutions\">See all solutions</a></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Developers</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Community</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Documentation</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Developer Tools</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Get Involved</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Utilities and Help</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Partners</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Become a Partner</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Marketplace</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 fHpLgU\"><a href=\"/pricing\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Pricing</span></a></li></ul><div class=\"Navigationstyles__StyledNavigationContent-sc-16d688r-4 eyrOGo\"><ul class=\"HeaderStyles__StyledCTALinks-sc-c7d2c41b-1 bUgInD\"><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 iWOgpY\" href=\"https://cloud.digitalocean.com/login\">Log in</a></li><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 bUvsjy\" href=\"https://cloud.digitalocean.com/registrations/new\">Sign up</a></li></ul></div></div><div class=\"Navigationstyles__StyledNavigationBackground-sc-16d688r-5 tRHjk\" style=\"height:0px\"></div></nav><div class=\"Headerstyles__StyledHeaderContent-sc-9ucsot-4 dkWskY\"><ul class=\"HeaderStyles__StyledCTALinks-sc-c7d2c41b-1 bUgInD\"><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 iWOgpY\" href=\"https://cloud.digitalocean.com/login\">Log in</a></li><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 bUvsjy\" href=\"https://cloud.digitalocean.com/registrations/new\">Sign up</a></li></ul></div><div class=\"Headerstyles__StyledDivider-sc-9ucsot-6 ihiyeA\"></div><button aria-label=\"Menu\" class=\"Headerstyles__StyledHeaderToggle-sc-9ucsot-3 dKbgtA\" type=\"button\"><svg fill=\"none\" height=\"32\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><line x1=\"3\" x2=\"21\" y1=\"12\" y2=\"12\"></line><line x1=\"3\" x2=\"21\" y1=\"6\" y2=\"6\"></line><line x1=\"3\" x2=\"21\" y1=\"18\" y2=\"18\"></line></svg></button></div></header><div class=\"Headerstyles__StyledBackdrop-sc-9ucsot-7 hUHGVR\"></div><div class=\"HeaderStyles__StyledProgressBar-sc-c7d2c41b-4 kcLFAK\"></div></div><!--$--><div class=\"Sectionstyles__StyledSection-sc-4l5hhw-0 ewQfcU blog-slug__StyledHeroSection-sc-3613c11f-4 hxLxqI\"><div class=\"Sectionstyles__StyledSectionInner-sc-4l5hhw-1 jQSBPu\"><div class=\"Containerstyles-sc-11hjsrs-0 dyboab blog-slug__StyledContainer-sc-3613c11f-0 hFxgiN\"><div class=\"Categorystyles__StyledCategory-sc-1lstx5-0 dMFUvU\" style=\"align-self:center\"><a href=\"/blog/tags/product-updates\">Product updates</a></div><div class=\"Containerstyles-sc-11hjsrs-0 hBhvxC\"><div class=\"Headlinestyles__StyledHeadline-sc-14uj3bo-1 hNsSJd\"><div class=\"Headlinestyles__StyledHeadlineHeader-sc-14uj3bo-2 bZmvnY\"><h1 class=\"Typographystyles-sc-o7qsl9-0 etpTBk\">OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing</h1></div></div></div><div class=\"blog-slug__StyledAuthorDetails-sc-3613c11f-1 gHtxhU\"><div><div class=\"AuthorsStyles__StyledAuthorList-sc-6bfa00a2-0 gGBXKw\"><div class=\"AuthorsStyles__StyledAvatarGroup-sc-6bfa00a2-2 fhNrjq\"><img alt=\"author\" class=\"AuthorsStyles__StyledAvatarSmall-sc-6bfa00a2-1 hBOddK\" data-nimg=\"1\" decoding=\"async\" height=\"48\" loading=\"lazy\" src=\"https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro\" srcset=\"https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro 1x, https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro 2x\" style=\"color:transparent\" width=\"48\"/></div><div class=\"AuthorsStyles__StyledAuthors-sc-6bfa00a2-4 fWmRpd\"><div class=\"AuthorsStyles__StyledAuthor-sc-6bfa00a2-3 bGdJmG\"><p class=\"Typographystyles-sc-o7qsl9-0 kZPABH\">By<!-- --> <a class=\"LinkInlinestyles-sc-18du0ds-0 eSWPpp\" href=\"/community/users/musamalik\">Musa Malik</a></p></div><p class=\"Typographystyles-sc-o7qsl9-0 dLrRIe\">AI/ML Engineer</p></div></div></div></div><div class=\"blog-slug__StyledMetaContainer-sc-3613c11f-2 ircmKC\"><ul class=\"blog-slug__StyledMetaInfo-sc-3613c11f-3 dxHuIJ\"><li class=\"Typographystyles-sc-o7qsl9-0 hzYaek\"><span class=\"Typographystyles-sc-o7qsl9-0 cOddxv\">Updated:<!-- --> <time datetime=\"2026-05-28T22:29:54.234Z\" itemprop=\"dateModified\">May 28, 2026</time></span></li><li class=\"Typographystyles-sc-o7qsl9-0 hzYaek\">3 min read</li></ul></div></div></div></div><div class=\"Sidebar__StyledSidebar-sc-b4087769-0 hAMSBs\"><div class=\"Sidebar__StyledSidebarContainer-sc-b4087769-7 ifJHgy\"><div class=\"Sidebar__StyledSidebarLeftSidebar-sc-b4087769-1 bQcpjB\"><div class=\"Sidebar__StyledSidebarLeftSidebarContainer-sc-b4087769-3 iqqBqJ\"><a class=\"Typographystyles-sc-o7qsl9-0 kZPABH Sidebar__StyledSidebarLeftSidebarLink-sc-b4087769-6 cJMIga\" href=\"/blog\">&lt;-<!-- --> <!-- -->Back to blog home</a><svg height=\"1\" preserveaspectratio=\"none\" viewbox=\"0 0 100 1\" width=\"100%\" xmlns=\"http://www.w3.org/2000/svg\"><line stroke=\"#8A96B5\" stroke-dasharray=\"4 8\" stroke-linecap=\"round\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" x1=\"0\" x2=\"100\" y1=\"0.5\" y2=\"0.5\"></line></svg><div class=\"TableOfContents__StyledTOC-sc-b456a232-0 izKMTV\"><nav aria-labelledby=\"_r_1_\" class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigation-sc-og8kyu-0 coZErM TableOfContents__StyledLargeSideNavigation-sc-b456a232-1 gnBxmL\"><h2 class=\"Typographystyles-sc-o7qsl9-0 KrLKk\" id=\"_r_1_\">Table of contents</h2><ol class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationContainer-sc-og8kyu-1 lfwyaB\"><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#what-is-an-inference-router\">What is an Inference Router?</a></span></li><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#what-changed-for-opencode\">What Changed for OpenCode</a></span></li><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#beyond-coding-agents\">Beyond Coding Agents</a></span></li></ol></nav><div class=\"TableOfContents__StyledSmallSideNavigation-sc-b456a232-2 ejWwSl\"><button class=\"TableOfContents__StyledTocButton-sc-b456a232-3 fVTOSb\">Table of contents<div class=\"TableOfContents__StyledTabbedSideNavigationIcon-sc-b456a232-6 cxOPrG\"><svg fill=\"none\" height=\"32\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><line x1=\"5\" x2=\"19\" y1=\"12\" y2=\"12\"></line><polyline points=\"12 5 19 12 12 19\"></polyline></svg></div></button><div class=\"TableOfContents__StyledTocDropdown-sc-b456a232-4 dwfzgi\"><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#what-is-an-inference-router\">What is an Inference Router?</a><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#what-changed-for-opencode\">What Changed for OpenCode</a><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#beyond-coding-agents\">Beyond Coding Agents</a></div></div></div></div></div><div class=\"Sidebar__StyledSidebarContent-sc-b4087769-5 hMcMnb\"><div class=\"blog-slug__StyledPostContent-sc-3613c11f-6 fiCMQz\"><div class=\"blog-slug__StyledPost-sc-3613c11f-5 dlNxMz\"><div class=\"Markdownstyles-sc-dd1icp-0 iceGPj MarkdownStyles__StyledMarkdown-sc-ee165f5-0 cWQrkY\"><p>Coding agents today have a massive spending problem. Every request, whether you’re designing system architecture or writing a single-line docstring, often gets routed to the same expensive frontier model. The result: unnecessary token usage, higher inference costs, and little awareness of task complexity or budget constraints.</p>\n<p>This high cost stems from a “one-size-fits-all” approach to model usage, where premium frontier models are utilized for trivial tasks that don’t require such intensive reasoning effort. In multi-agent workflows, where orchestrators delegate work to specialized subagents, this lack of discrimination frequently leads to runaway costs and opaque failure modes. Without intelligent routing, developers can essentially be forced into closed-provider lock-in and high API usage fees, which quickly escalate during exploratory building phases.</p>\n<p><a href=\"https://docs.digitalocean.com/products/inference/how-to/use-inference-router/\" rel=\"ugc nofollow noopener\" target=\"_blank\">DigitalOcean Inference Router</a>, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through <a href=\"https://opencode.ai/docs/providers/#digitalocean\" rel=\"ugc nofollow noopener\" target=\"_blank\">OpenCode</a>, the open-source AI coding agent, in as little as a few seconds.</p>\n<h2 id=\"what-is-an-inference-router\"><a href=\"#what-is-an-inference-router\" onclick=\"navigator.clipboard.writeText(this.href);\">What is an Inference Router?</a><a aria-hidden=\"true\" class=\"hash-anchor\" href=\"#what-is-an-inference-router\" onclick=\"navigator.clipboard.writeText(this.href);\"></a></h2>","truncated":true,"truncatedBy":"bytes","totalLines":47,"totalBytes":158521,"outputLines":7,"outputBytes":50712,"lastLinePartial":false,"firstLineExceedsLimit":false,"maxLines":2000,"maxBytes":51200}},"isError":false,"timestamp":1780219531845}}
{"type":"message","id":"b39aec93","parentId":"317eaf1c","timestamp":"2026-05-31T09:25:31.846Z","message":{"role":"toolResult","toolCallId":"call_iOIFuUQLnIPODEsVx1l8Wf5d|fc_0f029265420fe376016a1bfe8bab348191a15e2bb7cc7063f5","toolName":"read","content":[{"type":"text","text":"<!DOCTYPE html>\n\n<!DOCTYPE html>\n\n<html data-scroll-behavior=\"smooth\" lang=\"en\" style=\"--progress-width: 0%;\"><head><base href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\"/><style type=\"text/css\">.truste_caIcon_display {display: block !important;}</style><style type=\"text/css\">.truste_cursor_pointer {cursor: pointer;}.truste_border_none {border: none;}.truste_accessible_link {color: #1D4ED8;font-size: 14px;font-weight: 600;text-decoration: underline;}.truste_accessible_link:hover {color: #1D4ED8;text-decoration: none !important;}.truste_accessible_link:focus-visible {outline: none;border-radius: 4px;box-shadow: 0 0 0 1px #FFFFFF, 0 0 0 4px #3699F1;}</style><style type=\"text/css\">.ta-display-none { display: none; }</style><style type=\"text/css\"> .truste_caIcon_display, a#teconsent {color: #4d5b7c;font-family: Inter, sans-serif;font-size: 14px;font-weight: 400;}</style><meta charset=\"utf-8\"/><meta content=\"width=device-width, initial-scale=1, minimum-scale=1\" name=\"viewport\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/636a5ac981f94f8b.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/bb3ef058b751a6ad-s.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F007BlogBanners2024%2Fdatabases-1%28kiwi%29.png&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F007BlogBanners2024%2Fdatabases-1%28kiwi%29.png&amp;width=828 2x\" rel=\"preload\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2F0-BLOG-BANNERS%2Fapp-platform-1%28tulip%29%2520%281%29.png&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2F0-BLOG-BANNERS%2Fapp-platform-1%28tulip%29%2520%281%29.png&amp;width=828 2x\" rel=\"preload\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2Fgradient_ai_platform_blog_headers%2Fdo-1400x815%2520%281%29.svg&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2Fgradient_ai_platform_blog_headers%2Fdo-1400x815%2520%281%29.svg&amp;width=828 2x\" rel=\"preload\"/><link data-precedence=\"next\" href=\"file:///var/www/rss-offline/assets/c22ede05fdcde865/97a79e4b4aaa8dd0.css\" rel=\"stylesheet\"/><link data-precedence=\"next\" href=\"file:///var/www/rss-offline/assets/c22ede05fdcde865/8048675823972ead.css\" rel=\"stylesheet\"/><link data-precedence=\"next\" href=\"file:///var/www/rss-offline/assets/c22ede05fdcde865/3b31c6a23419a4de.css\" rel=\"stylesheet\"/><link as=\"script\" fetchpriority=\"low\" href=\"/_next/static/chunks/webpack-5acd975349974fcb.js\" rel=\"preload\"/><meta content=\"\" name=\"next-size-adjust\"/><meta content=\"#0069ff\" name=\"theme-color\"/><title>OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean</title><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" name=\"description\"/><link href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\" rel=\"canonical\"/><meta content=\"OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean\" property=\"og:title\"/><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" property=\"og:description\"/><meta content=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\" property=\"og:url\"/><meta content=\"https://doimages.nyc3.cdn.digitaloceanspaces.com/002Blog/gradient_ai_platform_blog_headers/news-2(lavender).png\" property=\"og:image\"/><meta content=\"summary_large_image\" name=\"twitter:card\"/><meta content=\"OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean\" name=\"twitter:title\"/><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" name=\"twitter:description\"/><meta content=\"https://doimages.nyc3.cdn.digitaloceanspaces.com/002Blog/gradient_ai_platform_blog_headers/news-2(lavender).png\" name=\"twitter:image\"/><link href=\"/_next/static/media/favicon.594d6067.ico\" rel=\"icon\" type=\"image/x-icon\"/><link href=\"/_next/static/media/favicon-16x16.71c34517.png\" rel=\"icon\" sizes=\"16x16\" type=\"image/png\"/><link href=\"/_next/static/media/favicon-32x32.b7ef9ede.png\" rel=\"icon\" sizes=\"32x32\" type=\"image/png\"/><link href=\"/_next/static/media/android-chrome-192x192.f09059d8.png\" rel=\"icon\" sizes=\"192x192\"/><link href=\"/_next/static/media/android-chrome-512x512.5f2e6221.png\" rel=\"icon\" sizes=\"512x512\"/><link href=\"/_next/static/media/apple-touch-icon.d7edaa01.png\" rel=\"apple-touch-icon\" sizes=\"180x180\"/><style data-styled=\"active\" data-styled-version=\"6.4.1\"></style><style id=\"ta-display-style\">/* */</style><style>body{margin-top:0!important}.rss-offline-banner{position:static;z-index:2147483647;background:#07111f;color:#fff;padding:12px 16px;font:14px/1.4 system-ui,sans-serif;box-shadow:0 1px 0 rgba(255,255,255,.08)}.rss-offline-banner a{color:#8dc3ff;text-decoration:none}.rss-offline-banner strong{color:#fff}.rss-offline-banner .muted{opacity:.78}</style><style>@page{size:auto;margin:12mm}html,body{background:#fff!important}body::before,body::after,*::before,*::after{backdrop-filter:none!important;filter:none!important}a{color:#0b57d0!important;text-decoration:none!important}[style*=\"position:fixed\"],[style*=\"position: sticky\"]{position:static!important}header nav footer aside[role=\"dialog\"]{display:none!important}</style></head><body><div class=\"rss-offline-banner\"><a href=\"file:///var/www/rss-offline/\">← rss-offline</a> · <a href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\">abrir original</a> · <strong>OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing [rendered]</strong> · <span class=\"muted\">DigitalOcean Blog · 2026-05-28 18:02 </span></div><div hidden=\"\"><!--$--><!--/$--></div><div class=\"NotificationsStyles__NotificationsContainer-sc-69d0afda-0 jTWtSQ\"></div><div class=\"Layout__StyledLayout-sc-9a47750a-0 bSJGQF\"><div class=\"HeaderStyles__StyledHeader-sc-c7d2c41b-0 dhpqCc\"><div aria-label=\"Top Bar\" class=\"TopBarstyles__StyledTopBar-sc-17n1n7p-0 iMEWEd\"><nav class=\"TopBarstyles__StyledTopBarContainer-sc-17n1n7p-1 dAVvrw\"><div class=\"TopBarstyles__StyledTopBarCTAs-sc-17n1n7p-2 dPzlrU\"><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/inference-router-architecture\" target=\"\">Inside the Architecture of DigitalOcean’s Inference Router<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 cjTPsg\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/introducing-digitalocean-ai-native-cloud\" target=\"\">Introducing DigitalOcean AI-Native Cloud<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/whats-new-on-inference-engine#week-of-may-4\" target=\"\">Now Available: Kimi K2.6<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.youtube.com/live/NwnW1zZZz7o\" target=\"_blank\">Missed the Deploy 2026 Keynote live? Watch it now and catch everything that was announced<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div></div><ul class=\"TopBarstyles__StyledTopBarLinks-sc-17n1n7p-3 finBGi\"><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/blog\">Blog</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"https://docs.digitalocean.com/products\">Docs</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/careers\">Careers</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/support\">Get Support</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/company/contact/sales?referrer=tophat\">Contact Sales</a></li></ul></nav></div><div class=\"Headerstyles__StyledBackdrop-sc-9ucsot-7 hUHGVR\"></div><header class=\"Headerstyles__StyledHeader-sc-9ucsot-0 hjBrds\"><div class=\"Headerstyles__StyledHeaderContainer-sc-9ucsot-1 fRyKcE\"><div class=\"Headerstyles__StyledHeaderLogo-sc-9ucsot-2 eALFRw\"><a href=\"/\"><span class=\"Typographystyles-sc-o7qsl9-0 jBwEfV\">DigitalOcean</span><div aria-hidden=\"true\" class=\"DigitalOceanStyles__StyledLogo-sc-5322d8c4-1 buQIZT\"><svg class=\"DigitalOceanSmileyStyles__StyledSvg-sc-64ca7b97-4 dYCpBs\" fill=\"none\" height=\"32\" tabindex=\"-1\" viewbox=\"0 0 90 90\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><circle class=\"DigitalOceanSmileyStyles__StyledCircle-sc-64ca7b97-0 bmFsHR\" cx=\"45\" cy=\"45\" fill=\"none\" r=\"36.25\" stroke=\"currentColor\" stroke-dasharray=\"170.824100538945 56.941366846315\" stroke-dashoffset=\"113.88273369263\" stroke-width=\"17.5\"></circle><rect class=\"DigitalOceanSmileyStyles__StyledPixelSm-sc-64ca7b97-1 yOpkM\" fill=\"currentColor\" height=\"11\" width=\"11\" x=\"3.25\" y=\"61.5\"></rect><rect class=\"DigitalOceanSmileyStyles__StyledPixelMd-sc-64ca7b97-2 kpiCiD\" fill=\"currentColor\" height=\"13.5\" width=\"13.5\" x=\"14.25\" y=\"72.5\"></rect><rect class=\"DigitalOceanSmileyStyles__StyledPixelLg-sc-64ca7b97-3 hLOPbg\" fill=\"currentColor\" height=\"17.25\" width=\"17.25\" x=\"27.5\" y=\"55.25\"></rect></svg><svg class=\"DigitalOceanStyles__StyledText-sc-5322d8c4-0 kGjwzS\" fill=\"currentColor\" height=\"32\" viewbox=\"32 0 159 32\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M57.242 9.305c-1.835-1.254-4.129-1.9-6.806-1.9h-5.812v18.399h5.812c2.677 0 4.971-.685 6.806-2.015.994-.722 1.797-1.711 2.333-2.965.535-1.255.841-2.699.841-4.334 0-1.635-.268-3.079-.841-4.296-.536-1.254-1.3-2.242-2.333-2.889Zm-9.213 1.179h1.835c2.027 0 3.709.38 4.971 1.178 1.415.837 2.141 2.471 2.141 4.79 0 2.395-.726 4.068-2.141 4.98-1.224.798-2.868 1.217-4.933 1.217h-1.835V10.484h-.038Zm16.479-3.346c-.574 0-1.032.191-1.415.571-.382.38-.612.836-.612 1.406s.192 1.027.574 1.445c.382.38.879.57 1.453.57.574 0 1.032-.19 1.453-.57.382-.38.574-.875.574-1.445 0-.57-.192-1.026-.574-1.406-.421-.342-.918-.571-1.453-.571Zm-1.683 5.664h3.288v12.963h-3.288zm15.177 1.103c-.994-.874-2.103-1.369-3.288-1.369-1.797 0-3.327.609-4.474 1.863-1.185 1.217-1.759 2.775-1.759 4.638 0 1.825.574 3.383 1.721 4.638 1.147 1.216 2.677 1.824 4.474 1.824 1.262 0 2.332-.342 3.25-1.026v.304c0 1.064-.306 1.901-.841 2.471-.574.57-1.377.874-2.333.874-1.529 0-2.447-.608-3.632-2.128l-2.218 2.128.076.076c.498.685 1.224 1.331 2.18 1.939.956.608 2.218.95 3.633.95 1.95 0 3.517-.608 4.664-1.786 1.148-1.179 1.759-2.775 1.759-4.714V12.803h-3.212v1.102Zm-.878 7.641c-.574.646-1.3.95-2.256.95-.956 0-1.683-.304-2.218-.95-.574-.646-.841-1.483-.841-2.547s.267-1.901.841-2.547c.574-.646 1.3-.95 2.218-.95.956 0 1.682.304 2.256.95.573.646.841 1.521.841 2.547.038 1.064-.268 1.901-.841 2.547Zm6.883-8.744h3.288v12.963h-3.288zm1.685-5.664c-.574 0-1.033.191-1.415.571-.382.38-.612.836-.612 1.406s.191 1.027.574 1.445c.382.38.879.57 1.453.57.573 0 1.032-.19 1.453-.57.382-.38.573-.875.573-1.445 0-.57-.191-1.026-.573-1.406-.421-.342-.88-.571-1.453-.571Zm8.793 2.167h-3.212v3.498h-1.874v2.965h1.874v5.36c0 1.672.344 2.889.994 3.573.689.684 1.874 1.064 3.518 1.064.535 0 1.071 0 1.606-.038h.153v-2.965l-1.109.076c-.765 0-1.3-.152-1.568-.418-.267-.266-.382-.836-.382-1.71v-4.904h3.059v-2.965h-3.059V9.305Zm18.43-1.939h3.288v18.4h-3.288v-18.4Zm36.364 13.762c-.574.646-1.186 1.216-1.645 1.52-.458.304-1.032.418-1.682.418-.956 0-1.759-.342-2.371-1.064-.612-.722-.956-1.635-.956-2.737s.306-2.015.918-2.737c.612-.722 1.415-1.064 2.371-1.064 1.07 0 2.179.646 3.135 1.786l2.18-2.053c-1.415-1.824-3.212-2.661-5.354-2.661-1.797 0-3.364.647-4.626 1.939-1.262 1.293-1.912 2.889-1.912 4.828 0 1.939.65 3.573 1.912 4.828 1.262 1.292 2.829 1.939 4.626 1.939 2.371 0 4.283-1.027 5.583-2.89l-2.179-2.052Zm13.459-6.501c-.459-.646-1.109-1.178-1.874-1.558-.765-.38-1.683-.57-2.715-.57-1.835 0-3.327.684-4.435 1.976-1.109 1.331-1.645 2.965-1.645 4.866 0 1.977.612 3.574 1.797 4.828 1.186 1.216 2.792 1.863 4.742 1.863 2.218 0 4.015-.875 5.391-2.623l.077-.076-2.141-2.053c-.192.228-.498.494-.727.76-.306.304-.612.532-.956.684a3.856 3.856 0 0 1-1.644.381c-.918 0-1.644-.266-2.218-.799-.535-.494-.841-1.14-.918-1.976h8.68l.039-1.179c0-.836-.115-1.635-.345-2.395-.305-.798-.65-1.482-1.108-2.129Zm-7.15 3.042a3.28 3.28 0 0 1 .841-1.521c.459-.418 1.032-.646 1.721-.646.803 0 1.414.228 1.835.684.382.418.612.912.65 1.521h-5.047v-.038Zm19.731-3.878c-.994-.836-2.37-1.255-4.091-1.255-1.109 0-2.103.229-3.021.685a5.297 5.297 0 0 0-2.218 2.09l.039.038 2.103 2.015c.879-1.368 1.835-1.862 3.097-1.862.688 0 1.262.19 1.682.532.421.342.65.798.65 1.406v.647c-.803-.229-1.606-.381-2.409-.381-1.644 0-2.944.381-3.938 1.141-.994.76-1.491 1.863-1.491 3.231 0 1.217.42 2.205 1.262 2.927.841.722 1.911 1.065 3.173 1.065s2.409-.495 3.48-1.369v1.064h3.212V17.44c-.039-1.596-.536-2.813-1.53-3.649Zm-5.813 7.032c.383-.266.88-.38 1.568-.38a7.31 7.31 0 0 1 2.485.457v1.254c-.726.646-1.682.988-2.829.988-.574 0-.994-.114-1.3-.38-.306-.228-.459-.532-.459-.95 0-.418.153-.76.535-.989Zm19.922-6.804c-.918-1.026-2.18-1.52-3.824-1.52-1.3 0-2.37.38-3.135 1.102v-.798h-3.212v12.963h3.288v-7.147c0-.988.23-1.749.689-2.319.458-.57 1.108-.836 1.95-.836.726 0 1.3.228 1.72.722.421.494.612 1.178.612 2.053v7.489h3.288v-7.489c0-1.787-.458-3.193-1.376-4.22Zm-80.64-.228c-.994-.836-2.371-1.255-4.091-1.255-1.109 0-2.103.229-3.021.685a5.291 5.291 0 0 0-2.218 2.09l.038.038 2.103 2.015c.88-1.368 1.836-1.862 3.098-1.862.688 0 1.261.19 1.682.532.421.342.65.798.65 1.406v.647c-.803-.229-1.606-.381-2.409-.381-1.644 0-2.944.381-3.938 1.141-.994.76-1.492 1.863-1.492 3.231 0 1.217.421 2.205 1.262 2.927s1.912 1.065 3.174 1.065 2.409-.495 3.48-1.369v1.064h3.211V17.44c-.038-1.596-.535-2.813-1.529-3.649Zm-5.813 7.032c.383-.266.88-.38 1.568-.38a7.31 7.31 0 0 1 2.485.457v1.254c-.726.646-1.682.988-2.829.988-.574 0-.994-.114-1.3-.38-.306-.228-.459-.532-.459-.95 0-.418.153-.76.535-.989Zm25.082 5.209c-5.238 0-9.521-4.258-9.521-9.466 0-5.208 4.283-9.466 9.521-9.466 5.239 0 9.521 4.258 9.521 9.466 0 5.208-4.244 9.466-9.521 9.466Zm-.001-15.624c-3.403 0-6.156 2.737-6.156 6.12s2.753 6.121 6.156 6.121c3.403 0 6.156-2.738 6.156-6.121 0-3.383-2.753-6.12-6.156-6.12Z\"></path></svg></div></a></div><nav aria-label=\"Main\" class=\"Navigationstyles__StyledNavigation-sc-16d688r-0 eEGMpp\" links=\"[object Object],[object Object],[object Object],[object Object],[object Object]\"><div class=\"Navigationstyles__StyledNavigationContainer-sc-16d688r-1 kwigWO\"><ul class=\"Navigationstyles__StyledNavigationList-sc-16d688r-2 fNSjwz\"><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Products</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Featured AI Products</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Compute</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Build, deploy, and scale cloud compute resources</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Containers and Images</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Safely store and manage containers and backups</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Managed Databases</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Fully managed resources running popular database engines</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Management and Dev Tools</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Control infrastructure and gather insights</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Networking</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Secure and control traffic to apps</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Security</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Help protect your account and resources with these security features</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Storage</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Store and access any amount of data reliably in the cloud</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Dropdownstyles__StyledButton-sc-16wobc6-10 ipGTcU\"><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 ghKDsS\" href=\"/products\">Browse all products</a></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Solutions</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">AI/ML</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">CMS</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Data and IoT</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Developer Tools</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Gaming and Media</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">GPU</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Hosting</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Security and Networking</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Startups and SMBs</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Web and App Platforms</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Dropdownstyles__StyledButton-sc-16wobc6-10 ipGTcU\"><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 ghKDsS\" href=\"/solutions\">See all solutions</a></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Developers</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Community</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Documentation</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Developer Tools</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Get Involved</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Utilities and Help</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Partners</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Become a Partner</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Marketplace</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 fHpLgU\"><a href=\"/pricing\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Pricing</span></a></li></ul><div class=\"Navigationstyles__StyledNavigationContent-sc-16d688r-4 eyrOGo\"><ul class=\"HeaderStyles__StyledCTALinks-sc-c7d2c41b-1 bUgInD\"><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 iWOgpY\" href=\"https://cloud.digitalocean.com/login\">Log in</a></li><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 bUvsjy\" href=\"https://cloud.digitalocean.com/registrations/new\">Sign up</a></li></ul></div></div><div class=\"Navigationstyles__StyledNavigationBackground-sc-16d688r-5 tRHjk\" style=\"height:0px\"></div></nav><div class=\"Headerstyles__StyledHeaderContent-sc-9ucsot-4 dkWskY\"><ul class=\"HeaderStyles__StyledCTALinks-sc-c7d2c41b-1 bUgInD\"><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 iWOgpY\" href=\"https://cloud.digitalocean.com/login\">Log in</a></li><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 bUvsjy\" href=\"https://cloud.digitalocean.com/registrations/new\">Sign up</a></li></ul></div><div class=\"Headerstyles__StyledDivider-sc-9ucsot-6 ihiyeA\"></div><button aria-label=\"Menu\" class=\"Headerstyles__StyledHeaderToggle-sc-9ucsot-3 dKbgtA\" type=\"button\"><svg fill=\"none\" height=\"32\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><line x1=\"3\" x2=\"21\" y1=\"12\" y2=\"12\"></line><line x1=\"3\" x2=\"21\" y1=\"6\" y2=\"6\"></line><line x1=\"3\" x2=\"21\" y1=\"18\" y2=\"18\"></line></svg></button></div></header><div class=\"Headerstyles__StyledBackdrop-sc-9ucsot-7 hUHGVR\"></div><div class=\"HeaderStyles__StyledProgressBar-sc-c7d2c41b-4 kcLFAK\"></div></div><!--$--><div class=\"Sectionstyles__StyledSection-sc-4l5hhw-0 ewQfcU blog-slug__StyledHeroSection-sc-3613c11f-4 hxLxqI\"><div class=\"Sectionstyles__StyledSectionInner-sc-4l5hhw-1 jQSBPu\"><div class=\"Containerstyles-sc-11hjsrs-0 dyboab blog-slug__StyledContainer-sc-3613c11f-0 hFxgiN\"><div class=\"Categorystyles__StyledCategory-sc-1lstx5-0 dMFUvU\" style=\"align-self:center\"><a href=\"/blog/tags/product-updates\">Product updates</a></div><div class=\"Containerstyles-sc-11hjsrs-0 hBhvxC\"><div class=\"Headlinestyles__StyledHeadline-sc-14uj3bo-1 hNsSJd\"><div class=\"Headlinestyles__StyledHeadlineHeader-sc-14uj3bo-2 bZmvnY\"><h1 class=\"Typographystyles-sc-o7qsl9-0 etpTBk\">OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing</h1></div></div></div><div class=\"blog-slug__StyledAuthorDetails-sc-3613c11f-1 gHtxhU\"><div><div class=\"AuthorsStyles__StyledAuthorList-sc-6bfa00a2-0 gGBXKw\"><div class=\"AuthorsStyles__StyledAvatarGroup-sc-6bfa00a2-2 fhNrjq\"><img alt=\"author\" class=\"AuthorsStyles__StyledAvatarSmall-sc-6bfa00a2-1 hBOddK\" data-nimg=\"1\" decoding=\"async\" height=\"48\" loading=\"lazy\" src=\"https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro\" srcset=\"https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro 1x, https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro 2x\" style=\"color:transparent\" width=\"48\"/></div><div class=\"AuthorsStyles__StyledAuthors-sc-6bfa00a2-4 fWmRpd\"><div class=\"AuthorsStyles__StyledAuthor-sc-6bfa00a2-3 bGdJmG\"><p class=\"Typographystyles-sc-o7qsl9-0 kZPABH\">By<!-- --> <a class=\"LinkInlinestyles-sc-18du0ds-0 eSWPpp\" href=\"/community/users/musamalik\">Musa Malik</a></p></div><p class=\"Typographystyles-sc-o7qsl9-0 dLrRIe\">AI/ML Engineer</p></div></div></div></div><div class=\"blog-slug__StyledMetaContainer-sc-3613c11f-2 ircmKC\"><ul class=\"blog-slug__StyledMetaInfo-sc-3613c11f-3 dxHuIJ\"><li class=\"Typographystyles-sc-o7qsl9-0 hzYaek\"><span class=\"Typographystyles-sc-o7qsl9-0 cOddxv\">Updated:<!-- --> <time datetime=\"2026-05-28T22:29:54.234Z\" itemprop=\"dateModified\">May 28, 2026</time></span></li><li class=\"Typographystyles-sc-o7qsl9-0 hzYaek\">3 min read</li></ul></div></div></div></div><div class=\"Sidebar__StyledSidebar-sc-b4087769-0 hAMSBs\"><div class=\"Sidebar__StyledSidebarContainer-sc-b4087769-7 ifJHgy\"><div class=\"Sidebar__StyledSidebarLeftSidebar-sc-b4087769-1 bQcpjB\"><div class=\"Sidebar__StyledSidebarLeftSidebarContainer-sc-b4087769-3 iqqBqJ\"><a class=\"Typographystyles-sc-o7qsl9-0 kZPABH Sidebar__StyledSidebarLeftSidebarLink-sc-b4087769-6 cJMIga\" href=\"/blog\">&lt;-<!-- --> <!-- -->Back to blog home</a><svg height=\"1\" preserveaspectratio=\"none\" viewbox=\"0 0 100 1\" width=\"100%\" xmlns=\"http://www.w3.org/2000/svg\"><line stroke=\"#8A96B5\" stroke-dasharray=\"4 8\" stroke-linecap=\"round\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" x1=\"0\" x2=\"100\" y1=\"0.5\" y2=\"0.5\"></line></svg><div class=\"TableOfContents__StyledTOC-sc-b456a232-0 izKMTV\"><nav aria-labelledby=\"_r_1_\" class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigation-sc-og8kyu-0 coZErM TableOfContents__StyledLargeSideNavigation-sc-b456a232-1 gnBxmL\"><h2 class=\"Typographystyles-sc-o7qsl9-0 KrLKk\" id=\"_r_1_\">Table of contents</h2><ol class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationContainer-sc-og8kyu-1 lfwyaB\"><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#what-is-an-inference-router\">What is an Inference Router?</a></span></li><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#what-changed-for-opencode\">What Changed for OpenCode</a></span></li><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#beyond-coding-agents\">Beyond Coding Agents</a></span></li></ol></nav><div class=\"TableOfContents__StyledSmallSideNavigation-sc-b456a232-2 ejWwSl\"><button class=\"TableOfContents__StyledTocButton-sc-b456a232-3 fVTOSb\">Table of contents<div class=\"TableOfContents__StyledTabbedSideNavigationIcon-sc-b456a232-6 cxOPrG\"><svg fill=\"none\" height=\"32\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><line x1=\"5\" x2=\"19\" y1=\"12\" y2=\"12\"></line><polyline points=\"12 5 19 12 12 19\"></polyline></svg></div></button><div class=\"TableOfContents__StyledTocDropdown-sc-b456a232-4 dwfzgi\"><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#what-is-an-inference-router\">What is an Inference Router?</a><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#what-changed-for-opencode\">What Changed for OpenCode</a><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#beyond-coding-agents\">Beyond Coding Agents</a></div></div></div></div></div><div class=\"Sidebar__StyledSidebarContent-sc-b4087769-5 hMcMnb\"><div class=\"blog-slug__StyledPostContent-sc-3613c11f-6 fiCMQz\"><div class=\"blog-slug__StyledPost-sc-3613c11f-5 dlNxMz\"><div class=\"Markdownstyles-sc-dd1icp-0 iceGPj MarkdownStyles__StyledMarkdown-sc-ee165f5-0 cWQrkY\"><p>Coding agents today have a massive spending problem. Every request, whether you’re designing system architecture or writing a single-line docstring, often gets routed to the same expensive frontier model. The result: unnecessary token usage, higher inference costs, and little awareness of task complexity or budget constraints.</p>\n<p>This high cost stems from a “one-size-fits-all” approach to model usage, where premium frontier models are utilized for trivial tasks that don’t require such intensive reasoning effort. In multi-agent workflows, where orchestrators delegate work to specialized subagents, this lack of discrimination frequently leads to runaway costs and opaque failure modes. Without intelligent routing, developers can essentially be forced into closed-provider lock-in and high API usage fees, which quickly escalate during exploratory building phases.</p>\n<p><a href=\"https://docs.digitalocean.com/products/inference/how-to/use-inference-router/\" rel=\"ugc nofollow noopener\" target=\"_blank\">DigitalOcean Inference Router</a>, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through <a href=\"https://opencode.ai/docs/providers/#digitalocean\" rel=\"ugc nofollow noopener\" target=\"_blank\">OpenCode</a>, the open-source AI coding agent, in as little as a few seconds.</p>\n<h2 id=\"what-is-an-inference-router\"><a href=\"#what-is-an-inference-router\" onclick=\"navigator.clipboard.writeText(this.href);\">What is an Inference Router?</a><a aria-hidden=\"true\" class=\"hash-anchor\" href=\"#what-is-an-inference-router\" onclick=\"navigator.clipboard.writeText(this.href);\"></a></h2>\n<p>An Inference Router is the auto-mode pattern engineers are used to, but with deliberate control over the tradeoffs that matter: latency, cost, and output quality. Rather than statically pointing your coding agent to a single model, an Inference Router can analyze each request and route it to the model best suited for that specific task. Not the <em>most</em> powerful model available, but the <em>right</em> model. That distinction is what drives real savings without compromising on your desired quality of output.</p>\n<p>To use DigitalOcean’s Inference Router: Create an Inference Router from the <a href=\"https://cloud.digitalocean.com/model-studio/router/catalog\" rel=\"ugc nofollow noopener\" target=\"_blank\">router catalog</a>—pick a preset or build a custom router via the API or UI. No GPU management, no infrastructure to run. Use it by setting “model”: “router:your-router-name” in any OpenAI-compatible API call.</p>\n<h2 id=\"what-changed-for-opencode\"><a href=\"#what-changed-for-opencode\" onclick=\"navigator.clipboard.writeText(this.href);\">What Changed for OpenCode</a><a aria-hidden=\"true\" class=\"hash-anchor\" href=\"#what-changed-for-opencode\" onclick=\"navigator.clipboard.writeText(this.href);\"></a></h2>\n<p>OpenCode has become one of the most popular AI coding harnesses on GitHub, <a href=\"https://github.com/anomalyco/opencode\" rel=\"ugc nofollow noopener\" target=\"_blank\">earning over 160,000 stars</a> by embracing a simple idea: developers should not be locked into a single model provider. Its rise has shown a demand for provider agnostic AI use cases. At Deploy 2026, Tyler Gillam - a core engineer on Inference Router - <a href=\"https://youtu.be/HYELtHc7QRo?si=JeUlp8DRHQh00utY&amp;t=2339\" rel=\"ugc nofollow noopener\" target=\"_blank\">demoed our integration live on stage</a>, showing exactly how OpenCode and Inference Router work together to make intelligent model selection decisions in real time. If you want to see it before diving in, the full recording is linked at the bottom of this post.</p>\n<p>Previously, integrating DigitalOcean models into OpenCode meant manually editing your <code>opencode.json</code>, adding each model by hand, a list that would be outdated within weeks given the pace of new model launches. So, we built a native OpenCode integration that supports Inference Routers and DigitalOcean Serverless Inference models right out of the box.</p>\n<p>Now you can run the following steps:</p>\n<ol>\n<li>Launch OpenCode (desktop, web or TUI) and run <code>/connect</code></li>\n<li>Select <strong>Login with DigitalOcean</strong></li>\n<li>Your Inference Routers are shown in the Model Selection tab</li>\n</ol>\n<p>That’s it. You’re plugging directly into a routing layer that’s already helping to make the cost &amp; quality tradeoff decisions for you based on your stated needs — with our purpose-build Software Engineering preset.</p>\n<h2 id=\"beyond-coding-agents\"><a href=\"#beyond-coding-agents\" onclick=\"navigator.clipboard.writeText(this.href);\">Beyond Coding Agents</a><a aria-hidden=\"true\" class=\"hash-anchor\" href=\"#beyond-coding-agents\" onclick=\"navigator.clipboard.writeText(this.href);\"></a></h2>\n<p>This integration is part of a broader effort to bring DigitalOcean’s Inference Engine into the tools developers already use, while continuing to invest in open source and upstream contributions. OpenCode is one example of that direction.</p>\n<p>The goal is to make intelligent, cost-aware model routing the default for coding agents, not something you have to manually configure and hope for the best. As the OSS model landscape keeps improving, routing intelligence will become more valuable, not less. The gap between “frontier” and “good enough” is closing fast, and developers who take advantage of routing will consistently come ahead on both desired quality and cost.</p>\n<p>If you’re using OpenCode, try <code>/connect</code> today. If you want to dig deeper on what Inference Router is and how it works, the full documentation is available below.</p>\n<p>Inference Router Resources:</p>\n<ul>\n<li><a href=\"/blog/inference-router-architecture\">How We Built DigitalOcean Inference Router</a></li>\n\n[Showing lines 1-27 of 35 (50.0KB limit). Use offset=28 to continue.]"}],"details":{"truncation":{"content":"<!DOCTYPE html>\n\n<!DOCTYPE html>\n\n<html data-scroll-behavior=\"smooth\" lang=\"en\" style=\"--progress-width: 0%;\"><head><base href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\"/><style type=\"text/css\">.truste_caIcon_display {display: block !important;}</style><style type=\"text/css\">.truste_cursor_pointer {cursor: pointer;}.truste_border_none {border: none;}.truste_accessible_link {color: #1D4ED8;font-size: 14px;font-weight: 600;text-decoration: underline;}.truste_accessible_link:hover {color: #1D4ED8;text-decoration: none !important;}.truste_accessible_link:focus-visible {outline: none;border-radius: 4px;box-shadow: 0 0 0 1px #FFFFFF, 0 0 0 4px #3699F1;}</style><style type=\"text/css\">.ta-display-none { display: none; }</style><style type=\"text/css\"> .truste_caIcon_display, a#teconsent {color: #4d5b7c;font-family: Inter, sans-serif;font-size: 14px;font-weight: 400;}</style><meta charset=\"utf-8\"/><meta content=\"width=device-width, initial-scale=1, minimum-scale=1\" name=\"viewport\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/636a5ac981f94f8b.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/bb3ef058b751a6ad-s.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"font\" crossorigin=\"\" href=\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\" rel=\"preload\" type=\"font/woff2\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F007BlogBanners2024%2Fdatabases-1%28kiwi%29.png&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F007BlogBanners2024%2Fdatabases-1%28kiwi%29.png&amp;width=828 2x\" rel=\"preload\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2F0-BLOG-BANNERS%2Fapp-platform-1%28tulip%29%2520%281%29.png&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2F0-BLOG-BANNERS%2Fapp-platform-1%28tulip%29%2520%281%29.png&amp;width=828 2x\" rel=\"preload\"/><link as=\"image\" imagesrcset=\"https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2Fgradient_ai_platform_blog_headers%2Fdo-1400x815%2520%281%29.svg&amp;width=640 1x, https://www.digitalocean.com/api/static-content/v1/images?src=https%3A%2F%2Fdoimages.nyc3.cdn.digitaloceanspaces.com%2F002Blog%2Fgradient_ai_platform_blog_headers%2Fdo-1400x815%2520%281%29.svg&amp;width=828 2x\" rel=\"preload\"/><link data-precedence=\"next\" href=\"file:///var/www/rss-offline/assets/c22ede05fdcde865/97a79e4b4aaa8dd0.css\" rel=\"stylesheet\"/><link data-precedence=\"next\" href=\"file:///var/www/rss-offline/assets/c22ede05fdcde865/8048675823972ead.css\" rel=\"stylesheet\"/><link data-precedence=\"next\" href=\"file:///var/www/rss-offline/assets/c22ede05fdcde865/3b31c6a23419a4de.css\" rel=\"stylesheet\"/><link as=\"script\" fetchpriority=\"low\" href=\"/_next/static/chunks/webpack-5acd975349974fcb.js\" rel=\"preload\"/><meta content=\"\" name=\"next-size-adjust\"/><meta content=\"#0069ff\" name=\"theme-color\"/><title>OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean</title><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" name=\"description\"/><link href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\" rel=\"canonical\"/><meta content=\"OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean\" property=\"og:title\"/><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" property=\"og:description\"/><meta content=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\" property=\"og:url\"/><meta content=\"https://doimages.nyc3.cdn.digitaloceanspaces.com/002Blog/gradient_ai_platform_blog_headers/news-2(lavender).png\" property=\"og:image\"/><meta content=\"summary_large_image\" name=\"twitter:card\"/><meta content=\"OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing | DigitalOcean\" name=\"twitter:title\"/><meta content=\"DigitalOcean Inference Router, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through OpenCode, the open-source AI coding agent, in as little as a few seconds.\" name=\"twitter:description\"/><meta content=\"https://doimages.nyc3.cdn.digitaloceanspaces.com/002Blog/gradient_ai_platform_blog_headers/news-2(lavender).png\" name=\"twitter:image\"/><link href=\"/_next/static/media/favicon.594d6067.ico\" rel=\"icon\" type=\"image/x-icon\"/><link href=\"/_next/static/media/favicon-16x16.71c34517.png\" rel=\"icon\" sizes=\"16x16\" type=\"image/png\"/><link href=\"/_next/static/media/favicon-32x32.b7ef9ede.png\" rel=\"icon\" sizes=\"32x32\" type=\"image/png\"/><link href=\"/_next/static/media/android-chrome-192x192.f09059d8.png\" rel=\"icon\" sizes=\"192x192\"/><link href=\"/_next/static/media/android-chrome-512x512.5f2e6221.png\" rel=\"icon\" sizes=\"512x512\"/><link href=\"/_next/static/media/apple-touch-icon.d7edaa01.png\" rel=\"apple-touch-icon\" sizes=\"180x180\"/><style data-styled=\"active\" data-styled-version=\"6.4.1\"></style><style id=\"ta-display-style\">/* */</style><style>body{margin-top:0!important}.rss-offline-banner{position:static;z-index:2147483647;background:#07111f;color:#fff;padding:12px 16px;font:14px/1.4 system-ui,sans-serif;box-shadow:0 1px 0 rgba(255,255,255,.08)}.rss-offline-banner a{color:#8dc3ff;text-decoration:none}.rss-offline-banner strong{color:#fff}.rss-offline-banner .muted{opacity:.78}</style><style>@page{size:auto;margin:12mm}html,body{background:#fff!important}body::before,body::after,*::before,*::after{backdrop-filter:none!important;filter:none!important}a{color:#0b57d0!important;text-decoration:none!important}[style*=\"position:fixed\"],[style*=\"position: sticky\"]{position:static!important}header nav footer aside[role=\"dialog\"]{display:none!important}</style></head><body><div class=\"rss-offline-banner\"><a href=\"file:///var/www/rss-offline/\">← rss-offline</a> · <a href=\"https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\">abrir original</a> · <strong>OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing [rendered]</strong> · <span class=\"muted\">DigitalOcean Blog · 2026-05-28 18:02 </span></div><div hidden=\"\"><!--$--><!--/$--></div><div class=\"NotificationsStyles__NotificationsContainer-sc-69d0afda-0 jTWtSQ\"></div><div class=\"Layout__StyledLayout-sc-9a47750a-0 bSJGQF\"><div class=\"HeaderStyles__StyledHeader-sc-c7d2c41b-0 dhpqCc\"><div aria-label=\"Top Bar\" class=\"TopBarstyles__StyledTopBar-sc-17n1n7p-0 iMEWEd\"><nav class=\"TopBarstyles__StyledTopBarContainer-sc-17n1n7p-1 dAVvrw\"><div class=\"TopBarstyles__StyledTopBarCTAs-sc-17n1n7p-2 dPzlrU\"><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/inference-router-architecture\" target=\"\">Inside the Architecture of DigitalOcean’s Inference Router<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 cjTPsg\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/introducing-digitalocean-ai-native-cloud\" target=\"\">Introducing DigitalOcean AI-Native Cloud<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.digitalocean.com/blog/whats-new-on-inference-engine#week-of-may-4\" target=\"\">Now Available: Kimi K2.6<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div><div class=\"TopBarCTAstyles__StyledTopBarCTA-sc-dwifql-0 laWOnA\"><p class=\"Typographystyles-sc-o7qsl9-0 kwBvlZ\"><a class=\"LinkTextstyles-sc-jz3jcd-0 jFwCpM\" href=\"https://www.youtube.com/live/NwnW1zZZz7o\" target=\"_blank\">Missed the Deploy 2026 Keynote live? Watch it now and catch everything that was announced<svg fill=\"none\" viewbox=\"0 0 18 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.6033 1.27344L16.6033 5.77344L12.6033 10.2734\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path><path d=\"M1.60327 5.77344H16.6033\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"></path></svg></a></p></div></div><ul class=\"TopBarstyles__StyledTopBarLinks-sc-17n1n7p-3 finBGi\"><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/blog\">Blog</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"https://docs.digitalocean.com/products\">Docs</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/careers\">Careers</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/support\">Get Support</a></li><li class=\"Typographystyles-sc-o7qsl9-0 jJgiqV\"><a href=\"/company/contact/sales?referrer=tophat\">Contact Sales</a></li></ul></nav></div><div class=\"Headerstyles__StyledBackdrop-sc-9ucsot-7 hUHGVR\"></div><header class=\"Headerstyles__StyledHeader-sc-9ucsot-0 hjBrds\"><div class=\"Headerstyles__StyledHeaderContainer-sc-9ucsot-1 fRyKcE\"><div class=\"Headerstyles__StyledHeaderLogo-sc-9ucsot-2 eALFRw\"><a href=\"/\"><span class=\"Typographystyles-sc-o7qsl9-0 jBwEfV\">DigitalOcean</span><div aria-hidden=\"true\" class=\"DigitalOceanStyles__StyledLogo-sc-5322d8c4-1 buQIZT\"><svg class=\"DigitalOceanSmileyStyles__StyledSvg-sc-64ca7b97-4 dYCpBs\" fill=\"none\" height=\"32\" tabindex=\"-1\" viewbox=\"0 0 90 90\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><circle class=\"DigitalOceanSmileyStyles__StyledCircle-sc-64ca7b97-0 bmFsHR\" cx=\"45\" cy=\"45\" fill=\"none\" r=\"36.25\" stroke=\"currentColor\" stroke-dasharray=\"170.824100538945 56.941366846315\" stroke-dashoffset=\"113.88273369263\" stroke-width=\"17.5\"></circle><rect class=\"DigitalOceanSmileyStyles__StyledPixelSm-sc-64ca7b97-1 yOpkM\" fill=\"currentColor\" height=\"11\" width=\"11\" x=\"3.25\" y=\"61.5\"></rect><rect class=\"DigitalOceanSmileyStyles__StyledPixelMd-sc-64ca7b97-2 kpiCiD\" fill=\"currentColor\" height=\"13.5\" width=\"13.5\" x=\"14.25\" y=\"72.5\"></rect><rect class=\"DigitalOceanSmileyStyles__StyledPixelLg-sc-64ca7b97-3 hLOPbg\" fill=\"currentColor\" height=\"17.25\" width=\"17.25\" x=\"27.5\" y=\"55.25\"></rect></svg><svg class=\"DigitalOceanStyles__StyledText-sc-5322d8c4-0 kGjwzS\" fill=\"currentColor\" height=\"32\" viewbox=\"32 0 159 32\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M57.242 9.305c-1.835-1.254-4.129-1.9-6.806-1.9h-5.812v18.399h5.812c2.677 0 4.971-.685 6.806-2.015.994-.722 1.797-1.711 2.333-2.965.535-1.255.841-2.699.841-4.334 0-1.635-.268-3.079-.841-4.296-.536-1.254-1.3-2.242-2.333-2.889Zm-9.213 1.179h1.835c2.027 0 3.709.38 4.971 1.178 1.415.837 2.141 2.471 2.141 4.79 0 2.395-.726 4.068-2.141 4.98-1.224.798-2.868 1.217-4.933 1.217h-1.835V10.484h-.038Zm16.479-3.346c-.574 0-1.032.191-1.415.571-.382.38-.612.836-.612 1.406s.192 1.027.574 1.445c.382.38.879.57 1.453.57.574 0 1.032-.19 1.453-.57.382-.38.574-.875.574-1.445 0-.57-.192-1.026-.574-1.406-.421-.342-.918-.571-1.453-.571Zm-1.683 5.664h3.288v12.963h-3.288zm15.177 1.103c-.994-.874-2.103-1.369-3.288-1.369-1.797 0-3.327.609-4.474 1.863-1.185 1.217-1.759 2.775-1.759 4.638 0 1.825.574 3.383 1.721 4.638 1.147 1.216 2.677 1.824 4.474 1.824 1.262 0 2.332-.342 3.25-1.026v.304c0 1.064-.306 1.901-.841 2.471-.574.57-1.377.874-2.333.874-1.529 0-2.447-.608-3.632-2.128l-2.218 2.128.076.076c.498.685 1.224 1.331 2.18 1.939.956.608 2.218.95 3.633.95 1.95 0 3.517-.608 4.664-1.786 1.148-1.179 1.759-2.775 1.759-4.714V12.803h-3.212v1.102Zm-.878 7.641c-.574.646-1.3.95-2.256.95-.956 0-1.683-.304-2.218-.95-.574-.646-.841-1.483-.841-2.547s.267-1.901.841-2.547c.574-.646 1.3-.95 2.218-.95.956 0 1.682.304 2.256.95.573.646.841 1.521.841 2.547.038 1.064-.268 1.901-.841 2.547Zm6.883-8.744h3.288v12.963h-3.288zm1.685-5.664c-.574 0-1.033.191-1.415.571-.382.38-.612.836-.612 1.406s.191 1.027.574 1.445c.382.38.879.57 1.453.57.573 0 1.032-.19 1.453-.57.382-.38.573-.875.573-1.445 0-.57-.191-1.026-.573-1.406-.421-.342-.88-.571-1.453-.571Zm8.793 2.167h-3.212v3.498h-1.874v2.965h1.874v5.36c0 1.672.344 2.889.994 3.573.689.684 1.874 1.064 3.518 1.064.535 0 1.071 0 1.606-.038h.153v-2.965l-1.109.076c-.765 0-1.3-.152-1.568-.418-.267-.266-.382-.836-.382-1.71v-4.904h3.059v-2.965h-3.059V9.305Zm18.43-1.939h3.288v18.4h-3.288v-18.4Zm36.364 13.762c-.574.646-1.186 1.216-1.645 1.52-.458.304-1.032.418-1.682.418-.956 0-1.759-.342-2.371-1.064-.612-.722-.956-1.635-.956-2.737s.306-2.015.918-2.737c.612-.722 1.415-1.064 2.371-1.064 1.07 0 2.179.646 3.135 1.786l2.18-2.053c-1.415-1.824-3.212-2.661-5.354-2.661-1.797 0-3.364.647-4.626 1.939-1.262 1.293-1.912 2.889-1.912 4.828 0 1.939.65 3.573 1.912 4.828 1.262 1.292 2.829 1.939 4.626 1.939 2.371 0 4.283-1.027 5.583-2.89l-2.179-2.052Zm13.459-6.501c-.459-.646-1.109-1.178-1.874-1.558-.765-.38-1.683-.57-2.715-.57-1.835 0-3.327.684-4.435 1.976-1.109 1.331-1.645 2.965-1.645 4.866 0 1.977.612 3.574 1.797 4.828 1.186 1.216 2.792 1.863 4.742 1.863 2.218 0 4.015-.875 5.391-2.623l.077-.076-2.141-2.053c-.192.228-.498.494-.727.76-.306.304-.612.532-.956.684a3.856 3.856 0 0 1-1.644.381c-.918 0-1.644-.266-2.218-.799-.535-.494-.841-1.14-.918-1.976h8.68l.039-1.179c0-.836-.115-1.635-.345-2.395-.305-.798-.65-1.482-1.108-2.129Zm-7.15 3.042a3.28 3.28 0 0 1 .841-1.521c.459-.418 1.032-.646 1.721-.646.803 0 1.414.228 1.835.684.382.418.612.912.65 1.521h-5.047v-.038Zm19.731-3.878c-.994-.836-2.37-1.255-4.091-1.255-1.109 0-2.103.229-3.021.685a5.297 5.297 0 0 0-2.218 2.09l.039.038 2.103 2.015c.879-1.368 1.835-1.862 3.097-1.862.688 0 1.262.19 1.682.532.421.342.65.798.65 1.406v.647c-.803-.229-1.606-.381-2.409-.381-1.644 0-2.944.381-3.938 1.141-.994.76-1.491 1.863-1.491 3.231 0 1.217.42 2.205 1.262 2.927.841.722 1.911 1.065 3.173 1.065s2.409-.495 3.48-1.369v1.064h3.212V17.44c-.039-1.596-.536-2.813-1.53-3.649Zm-5.813 7.032c.383-.266.88-.38 1.568-.38a7.31 7.31 0 0 1 2.485.457v1.254c-.726.646-1.682.988-2.829.988-.574 0-.994-.114-1.3-.38-.306-.228-.459-.532-.459-.95 0-.418.153-.76.535-.989Zm19.922-6.804c-.918-1.026-2.18-1.52-3.824-1.52-1.3 0-2.37.38-3.135 1.102v-.798h-3.212v12.963h3.288v-7.147c0-.988.23-1.749.689-2.319.458-.57 1.108-.836 1.95-.836.726 0 1.3.228 1.72.722.421.494.612 1.178.612 2.053v7.489h3.288v-7.489c0-1.787-.458-3.193-1.376-4.22Zm-80.64-.228c-.994-.836-2.371-1.255-4.091-1.255-1.109 0-2.103.229-3.021.685a5.291 5.291 0 0 0-2.218 2.09l.038.038 2.103 2.015c.88-1.368 1.836-1.862 3.098-1.862.688 0 1.261.19 1.682.532.421.342.65.798.65 1.406v.647c-.803-.229-1.606-.381-2.409-.381-1.644 0-2.944.381-3.938 1.141-.994.76-1.492 1.863-1.492 3.231 0 1.217.421 2.205 1.262 2.927s1.912 1.065 3.174 1.065 2.409-.495 3.48-1.369v1.064h3.211V17.44c-.038-1.596-.535-2.813-1.529-3.649Zm-5.813 7.032c.383-.266.88-.38 1.568-.38a7.31 7.31 0 0 1 2.485.457v1.254c-.726.646-1.682.988-2.829.988-.574 0-.994-.114-1.3-.38-.306-.228-.459-.532-.459-.95 0-.418.153-.76.535-.989Zm25.082 5.209c-5.238 0-9.521-4.258-9.521-9.466 0-5.208 4.283-9.466 9.521-9.466 5.239 0 9.521 4.258 9.521 9.466 0 5.208-4.244 9.466-9.521 9.466Zm-.001-15.624c-3.403 0-6.156 2.737-6.156 6.12s2.753 6.121 6.156 6.121c3.403 0 6.156-2.738 6.156-6.121 0-3.383-2.753-6.12-6.156-6.12Z\"></path></svg></div></a></div><nav aria-label=\"Main\" class=\"Navigationstyles__StyledNavigation-sc-16d688r-0 eEGMpp\" links=\"[object Object],[object Object],[object Object],[object Object],[object Object]\"><div class=\"Navigationstyles__StyledNavigationContainer-sc-16d688r-1 kwigWO\"><ul class=\"Navigationstyles__StyledNavigationList-sc-16d688r-2 fNSjwz\"><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Products</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Featured AI Products</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Compute</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Build, deploy, and scale cloud compute resources</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Containers and Images</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Safely store and manage containers and backups</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Managed Databases</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Fully managed resources running popular database engines</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Management and Dev Tools</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Control infrastructure and gather insights</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Networking</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Secure and control traffic to apps</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Security</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Help protect your account and resources with these security features</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Storage</p></div><div class=\"Cardstyles__StyledCardContent-sc-ngsvtw-4 ionGct\"><p class=\"Typographystyles-sc-o7qsl9-0 hztkkT\">Store and access any amount of data reliably in the cloud</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Dropdownstyles__StyledButton-sc-16wobc6-10 ipGTcU\"><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 ghKDsS\" href=\"/products\">Browse all products</a></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Solutions</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">AI/ML</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">CMS</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Data and IoT</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Developer Tools</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Gaming and Media</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">GPU</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Hosting</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Security and Networking</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Startups and SMBs</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Web and App Platforms</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Dropdownstyles__StyledButton-sc-16wobc6-10 ipGTcU\"><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 ghKDsS\" href=\"/solutions\">See all solutions</a></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Developers</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Community</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Documentation</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Developer Tools</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Get Involved</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Utilities and Help</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 cInbxh\"><button aria-expanded=\"false\" type=\"button\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Partners</span><svg fill=\"none\" height=\"20\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"20\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg></button><div class=\"Dropdownstyles__StyledDropdown-sc-16wobc6-1 eBiTbl\"><div class=\"Dropdownstyles__StyledDropdownContainer-sc-16wobc6-0 jYiNoG\"><div class=\"Dropdownstyles__StyledBreadcrumbContainer-sc-16wobc6-12 bCoZkl\"></div><div class=\"Dropdownstyles__StyledFeature-sc-16wobc6-6 ilHKxr\"><div class=\"Dropdownstyles__StyledFeatureInner-sc-16wobc6-7 kEmKKX\"><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Become a Partner</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div><div class=\"Cardstyles__StyledCard-sc-ngsvtw-1 gzVioq\"><div class=\"Cardstyles__StyledCardContentContainer-sc-ngsvtw-2 ckoRLz\"><div class=\"Cardstyles__StyledCardHeading-sc-ngsvtw-3 glLKFJ\"><p class=\"Typographystyles-sc-o7qsl9-0 ehPOKK\">Marketplace</p></div></div><div class=\"Cardstyles__StyledChevron-sc-ngsvtw-0 UiLVR\"><svg fill=\"none\" height=\"16\" stroke=\"var(--icon-primary)\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"16\" xmlns=\"http://www.w3.org/2000/svg\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg></div></div></div></div><div class=\"Dropdownstyles__StyledDropdownContent-sc-16wobc6-8 iYyZce\"><div class=\"Dropdownstyles__StyledDropdownHeadingContainer-sc-16wobc6-4 hNcsZn\"><h3 class=\"Typographystyles-sc-o7qsl9-0 hfWCIw\"></h3></div><div class=\"Gridstyles-sc-isu2n3-0 iigAtF\"></div></div></div></div></li><li class=\"Navigationstyles__StyledNavigationItem-sc-16d688r-3 fHpLgU\"><a href=\"/pricing\"><span class=\"Typographystyles-sc-o7qsl9-0 iRNPlK\">Pricing</span></a></li></ul><div class=\"Navigationstyles__StyledNavigationContent-sc-16d688r-4 eyrOGo\"><ul class=\"HeaderStyles__StyledCTALinks-sc-c7d2c41b-1 bUgInD\"><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 iWOgpY\" href=\"https://cloud.digitalocean.com/login\">Log in</a></li><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 bUvsjy\" href=\"https://cloud.digitalocean.com/registrations/new\">Sign up</a></li></ul></div></div><div class=\"Navigationstyles__StyledNavigationBackground-sc-16d688r-5 tRHjk\" style=\"height:0px\"></div></nav><div class=\"Headerstyles__StyledHeaderContent-sc-9ucsot-4 dkWskY\"><ul class=\"HeaderStyles__StyledCTALinks-sc-c7d2c41b-1 bUgInD\"><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 iWOgpY\" href=\"https://cloud.digitalocean.com/login\">Log in</a></li><li><a aria-disabled=\"false\" class=\"Buttonstyles-sc-hznqte-1 bUvsjy\" href=\"https://cloud.digitalocean.com/registrations/new\">Sign up</a></li></ul></div><div class=\"Headerstyles__StyledDivider-sc-9ucsot-6 ihiyeA\"></div><button aria-label=\"Menu\" class=\"Headerstyles__StyledHeaderToggle-sc-9ucsot-3 dKbgtA\" type=\"button\"><svg fill=\"none\" height=\"32\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><line x1=\"3\" x2=\"21\" y1=\"12\" y2=\"12\"></line><line x1=\"3\" x2=\"21\" y1=\"6\" y2=\"6\"></line><line x1=\"3\" x2=\"21\" y1=\"18\" y2=\"18\"></line></svg></button></div></header><div class=\"Headerstyles__StyledBackdrop-sc-9ucsot-7 hUHGVR\"></div><div class=\"HeaderStyles__StyledProgressBar-sc-c7d2c41b-4 kcLFAK\"></div></div><!--$--><div class=\"Sectionstyles__StyledSection-sc-4l5hhw-0 ewQfcU blog-slug__StyledHeroSection-sc-3613c11f-4 hxLxqI\"><div class=\"Sectionstyles__StyledSectionInner-sc-4l5hhw-1 jQSBPu\"><div class=\"Containerstyles-sc-11hjsrs-0 dyboab blog-slug__StyledContainer-sc-3613c11f-0 hFxgiN\"><div class=\"Categorystyles__StyledCategory-sc-1lstx5-0 dMFUvU\" style=\"align-self:center\"><a href=\"/blog/tags/product-updates\">Product updates</a></div><div class=\"Containerstyles-sc-11hjsrs-0 hBhvxC\"><div class=\"Headlinestyles__StyledHeadline-sc-14uj3bo-1 hNsSJd\"><div class=\"Headlinestyles__StyledHeadlineHeader-sc-14uj3bo-2 bZmvnY\"><h1 class=\"Typographystyles-sc-o7qsl9-0 etpTBk\">OpenCode Now Supports DigitalOcean Inference Router for Intelligent Model Routing</h1></div></div></div><div class=\"blog-slug__StyledAuthorDetails-sc-3613c11f-1 gHtxhU\"><div><div class=\"AuthorsStyles__StyledAuthorList-sc-6bfa00a2-0 gGBXKw\"><div class=\"AuthorsStyles__StyledAvatarGroup-sc-6bfa00a2-2 fhNrjq\"><img alt=\"author\" class=\"AuthorsStyles__StyledAvatarSmall-sc-6bfa00a2-1 hBOddK\" data-nimg=\"1\" decoding=\"async\" height=\"48\" loading=\"lazy\" src=\"https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro\" srcset=\"https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro 1x, https://www.gravatar.com/avatar/6c29b11c4ac98017df02463d0c637bce9d9d157ae7b1c5a02c9d5f2f93a5d563?default=retro 2x\" style=\"color:transparent\" width=\"48\"/></div><div class=\"AuthorsStyles__StyledAuthors-sc-6bfa00a2-4 fWmRpd\"><div class=\"AuthorsStyles__StyledAuthor-sc-6bfa00a2-3 bGdJmG\"><p class=\"Typographystyles-sc-o7qsl9-0 kZPABH\">By<!-- --> <a class=\"LinkInlinestyles-sc-18du0ds-0 eSWPpp\" href=\"/community/users/musamalik\">Musa Malik</a></p></div><p class=\"Typographystyles-sc-o7qsl9-0 dLrRIe\">AI/ML Engineer</p></div></div></div></div><div class=\"blog-slug__StyledMetaContainer-sc-3613c11f-2 ircmKC\"><ul class=\"blog-slug__StyledMetaInfo-sc-3613c11f-3 dxHuIJ\"><li class=\"Typographystyles-sc-o7qsl9-0 hzYaek\"><span class=\"Typographystyles-sc-o7qsl9-0 cOddxv\">Updated:<!-- --> <time datetime=\"2026-05-28T22:29:54.234Z\" itemprop=\"dateModified\">May 28, 2026</time></span></li><li class=\"Typographystyles-sc-o7qsl9-0 hzYaek\">3 min read</li></ul></div></div></div></div><div class=\"Sidebar__StyledSidebar-sc-b4087769-0 hAMSBs\"><div class=\"Sidebar__StyledSidebarContainer-sc-b4087769-7 ifJHgy\"><div class=\"Sidebar__StyledSidebarLeftSidebar-sc-b4087769-1 bQcpjB\"><div class=\"Sidebar__StyledSidebarLeftSidebarContainer-sc-b4087769-3 iqqBqJ\"><a class=\"Typographystyles-sc-o7qsl9-0 kZPABH Sidebar__StyledSidebarLeftSidebarLink-sc-b4087769-6 cJMIga\" href=\"/blog\">&lt;-<!-- --> <!-- -->Back to blog home</a><svg height=\"1\" preserveaspectratio=\"none\" viewbox=\"0 0 100 1\" width=\"100%\" xmlns=\"http://www.w3.org/2000/svg\"><line stroke=\"#8A96B5\" stroke-dasharray=\"4 8\" stroke-linecap=\"round\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" x1=\"0\" x2=\"100\" y1=\"0.5\" y2=\"0.5\"></line></svg><div class=\"TableOfContents__StyledTOC-sc-b456a232-0 izKMTV\"><nav aria-labelledby=\"_r_1_\" class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigation-sc-og8kyu-0 coZErM TableOfContents__StyledLargeSideNavigation-sc-b456a232-1 gnBxmL\"><h2 class=\"Typographystyles-sc-o7qsl9-0 KrLKk\" id=\"_r_1_\">Table of contents</h2><ol class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationContainer-sc-og8kyu-1 lfwyaB\"><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#what-is-an-inference-router\">What is an Inference Router?</a></span></li><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#what-changed-for-opencode\">What Changed for OpenCode</a></span></li><li class=\"TabbedSideNavigationstyles__StyledTabbedSideNavigationItem-sc-og8kyu-3 ecujID\"><span class=\"Typographystyles-sc-o7qsl9-0 dfVQWq\"><a href=\"#beyond-coding-agents\">Beyond Coding Agents</a></span></li></ol></nav><div class=\"TableOfContents__StyledSmallSideNavigation-sc-b456a232-2 ejWwSl\"><button class=\"TableOfContents__StyledTocButton-sc-b456a232-3 fVTOSb\">Table of contents<div class=\"TableOfContents__StyledTabbedSideNavigationIcon-sc-b456a232-6 cxOPrG\"><svg fill=\"none\" height=\"32\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewbox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\"><line x1=\"5\" x2=\"19\" y1=\"12\" y2=\"12\"></line><polyline points=\"12 5 19 12 12 19\"></polyline></svg></div></button><div class=\"TableOfContents__StyledTocDropdown-sc-b456a232-4 dwfzgi\"><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#what-is-an-inference-router\">What is an Inference Router?</a><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#what-changed-for-opencode\">What Changed for OpenCode</a><a class=\"LinkInlinestyles-sc-18du0ds-0 bxzPVF TableOfContents__StyledTocItem-sc-b456a232-5 cXVZi\" href=\"#beyond-coding-agents\">Beyond Coding Agents</a></div></div></div></div></div><div class=\"Sidebar__StyledSidebarContent-sc-b4087769-5 hMcMnb\"><div class=\"blog-slug__StyledPostContent-sc-3613c11f-6 fiCMQz\"><div class=\"blog-slug__StyledPost-sc-3613c11f-5 dlNxMz\"><div class=\"Markdownstyles-sc-dd1icp-0 iceGPj MarkdownStyles__StyledMarkdown-sc-ee165f5-0 cWQrkY\"><p>Coding agents today have a massive spending problem. Every request, whether you’re designing system architecture or writing a single-line docstring, often gets routed to the same expensive frontier model. The result: unnecessary token usage, higher inference costs, and little awareness of task complexity or budget constraints.</p>\n<p>This high cost stems from a “one-size-fits-all” approach to model usage, where premium frontier models are utilized for trivial tasks that don’t require such intensive reasoning effort. In multi-agent workflows, where orchestrators delegate work to specialized subagents, this lack of discrimination frequently leads to runaway costs and opaque failure modes. Without intelligent routing, developers can essentially be forced into closed-provider lock-in and high API usage fees, which quickly escalate during exploratory building phases.</p>\n<p><a href=\"https://docs.digitalocean.com/products/inference/how-to/use-inference-router/\" rel=\"ugc nofollow noopener\" target=\"_blank\">DigitalOcean Inference Router</a>, now in Public Preview, was built to solve this problem by dynamically routing requests to the right model for the job. As part of DigitalOcean’s AI-Native Cloud, it gives developers a unified way to control, optimize, and evaluate AI inference across models. And as of today, you can access it through <a href=\"https://opencode.ai/docs/providers/#digitalocean\" rel=\"ugc nofollow noopener\" target=\"_blank\">OpenCode</a>, the open-source AI coding agent, in as little as a few seconds.</p>\n<h2 id=\"what-is-an-inference-router\"><a href=\"#what-is-an-inference-router\" onclick=\"navigator.clipboard.writeText(this.href);\">What is an Inference Router?</a><a aria-hidden=\"true\" class=\"hash-anchor\" href=\"#what-is-an-inference-router\" onclick=\"navigator.clipboard.writeText(this.href);\"></a></h2>\n<p>An Inference Router is the auto-mode pattern engineers are used to, but with deliberate control over the tradeoffs that matter: latency, cost, and output quality. Rather than statically pointing your coding agent to a single model, an Inference Router can analyze each request and route it to the model best suited for that specific task. Not the <em>most</em> powerful model available, but the <em>right</em> model. That distinction is what drives real savings without compromising on your desired quality of output.</p>\n<p>To use DigitalOcean’s Inference Router: Create an Inference Router from the <a href=\"https://cloud.digitalocean.com/model-studio/router/catalog\" rel=\"ugc nofollow noopener\" target=\"_blank\">router catalog</a>—pick a preset or build a custom router via the API or UI. No GPU management, no infrastructure to run. Use it by setting “model”: “router:your-router-name” in any OpenAI-compatible API call.</p>\n<h2 id=\"what-changed-for-opencode\"><a href=\"#what-changed-for-opencode\" onclick=\"navigator.clipboard.writeText(this.href);\">What Changed for OpenCode</a><a aria-hidden=\"true\" class=\"hash-anchor\" href=\"#what-changed-for-opencode\" onclick=\"navigator.clipboard.writeText(this.href);\"></a></h2>\n<p>OpenCode has become one of the most popular AI coding harnesses on GitHub, <a href=\"https://github.com/anomalyco/opencode\" rel=\"ugc nofollow noopener\" target=\"_blank\">earning over 160,000 stars</a> by embracing a simple idea: developers should not be locked into a single model provider. Its rise has shown a demand for provider agnostic AI use cases. At Deploy 2026, Tyler Gillam - a core engineer on Inference Router - <a href=\"https://youtu.be/HYELtHc7QRo?si=JeUlp8DRHQh00utY&amp;t=2339\" rel=\"ugc nofollow noopener\" target=\"_blank\">demoed our integration live on stage</a>, showing exactly how OpenCode and Inference Router work together to make intelligent model selection decisions in real time. If you want to see it before diving in, the full recording is linked at the bottom of this post.</p>\n<p>Previously, integrating DigitalOcean models into OpenCode meant manually editing your <code>opencode.json</code>, adding each model by hand, a list that would be outdated within weeks given the pace of new model launches. So, we built a native OpenCode integration that supports Inference Routers and DigitalOcean Serverless Inference models right out of the box.</p>\n<p>Now you can run the following steps:</p>\n<ol>\n<li>Launch OpenCode (desktop, web or TUI) and run <code>/connect</code></li>\n<li>Select <strong>Login with DigitalOcean</strong></li>\n<li>Your Inference Routers are shown in the Model Selection tab</li>\n</ol>\n<p>That’s it. You’re plugging directly into a routing layer that’s already helping to make the cost &amp; quality tradeoff decisions for you based on your stated needs — with our purpose-build Software Engineering preset.</p>\n<h2 id=\"beyond-coding-agents\"><a href=\"#beyond-coding-agents\" onclick=\"navigator.clipboard.writeText(this.href);\">Beyond Coding Agents</a><a aria-hidden=\"true\" class=\"hash-anchor\" href=\"#beyond-coding-agents\" onclick=\"navigator.clipboard.writeText(this.href);\"></a></h2>\n<p>This integration is part of a broader effort to bring DigitalOcean’s Inference Engine into the tools developers already use, while continuing to invest in open source and upstream contributions. OpenCode is one example of that direction.</p>\n<p>The goal is to make intelligent, cost-aware model routing the default for coding agents, not something you have to manually configure and hope for the best. As the OSS model landscape keeps improving, routing intelligence will become more valuable, not less. The gap between “frontier” and “good enough” is closing fast, and developers who take advantage of routing will consistently come ahead on both desired quality and cost.</p>\n<p>If you’re using OpenCode, try <code>/connect</code> today. If you want to dig deeper on what Inference Router is and how it works, the full documentation is available below.</p>\n<p>Inference Router Resources:</p>\n<ul>\n<li><a href=\"/blog/inference-router-architecture\">How We Built DigitalOcean Inference Router</a></li>","truncated":true,"truncatedBy":"bytes","totalLines":35,"totalBytes":119847,"outputLines":27,"outputBytes":51046,"lastLinePartial":false,"firstLineExceedsLimit":false,"maxLines":2000,"maxBytes":51200}},"isError":false,"timestamp":1780219531846}}
{"type":"message","id":"f2e9b6be","parentId":"b39aec93","timestamp":"2026-05-31T09:25:38.746Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Improving builder targeting**\n\nI'm thinking I need to enhance the targeting for the builder. I should probably check the GitHub body for that — maybe focus on the article tag. Using tools like grep with BeautifulSoup could help here. My goal is to identify unique selectors to refine the targeting. All of this should help in making the process much smoother. Let’s see how it goes!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bfe8d3a38819197fef69b709be1bd\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_6P5Z1hIrMeQ1XC0vVtoJZY1D_I_pR9NDgi3zRNJFDTgiczKiZzqew5gvQuk2hvljnko_y0MtzeMHoE_6gPR_jdVqodnjcY9rBnsMogEdgnRvbc8Kz_4f4fUYL4TqufvNWdkC2HXczTjaA0xmZncgaT1SQi7pqjHoKzD-y1aV5wmbdfcej9N13aU-Nett311pOL9MymPZrXjwI56emYHfbKjFQGKmjaPkR12CnrjXFw6KxU48IXsX22JO6_t0lWLO9pRo5zS3nZImyha19hQSM8dnOIcz8_Va5VAJJ8ij6jdwAncBTGOimMQJQvPsWKFwkSdR-JuriAaGlWdutgrn_8lX-7ymG0RtLSQRhl6LdSxuuq-P6Hq97NYBiWsJLlsf6lMGbdv7gEzsxGMKhqPIA5tnhFGf2XDfZfQejQpN5MI9KyEsq3y1pYnbzyJweVUT5XA1DnQKgxF7HYasOpnZkB1s5utDVqwBjcVngEVK1GmfPEAn6Pdg8_sHADz9mIIoMd5QnSCqUNAcAqpAjeYSn3xaTYm8g7viaU0jdtAtJUD5L2b0gFxGdp8FOWWTcW3kG3pCt_AGdZ45W6tC-BGhyVHe5ZnP-h9H_3roL-1EMv9Tj7ZBh2VP4YS8iApVpJOZJylnapddQD1NjeD8xHoBCMmkC3pDXfKOL5fVeiuDMi0z6OmQtoRqV3SEjXvRnHSV5BJ5smdmqqYt7lbGtMYhNc2uPfEvVOq9r56KSvFrHkwfEkkEIu7zNVYSNHHnIPGgE2iqG00PfnKNEvrARr4HdS3P4Hlfb1RFzb5260HTIN5qtG5navah1-zOzMuwaB8Pbbh95T85GL0gP4rnKt6yGP2CnmlLbnSQUNEDOgPs6Gm1aVMr4Gcd8RLCs9h-7yVKqFj17AkdEE1E05vzQVmUg72jIoBtrU-MFzSqbFPp-uprBmTE13QvCMPYtSI2p5TpfMtQYplNP6LaHYFE91kiERYeaNKaLrre8A174mPqrZVFuuxILuwOSGQUe05qY1sdrKC3HPzYINtTizMLSyvOioZuMu68jc5RFosPdLF12zfwdz7s5RdcIX0Xux8iYNv_CM5t9B1Z4JEzZeSCw5X_VmsA==\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Improving builder targeting**\\n\\nI'm thinking I need to enhance the targeting for the builder. I should probably check the GitHub body for that — maybe focus on the article tag. Using tools like grep with BeautifulSoup could help here. My goal is to identify unique selectors to refine the targeting. All of this should help in making the process much smoother. Let’s see how it goes!\"}]}"},{"type":"toolCall","id":"call_oMj0pGLSGf9LppPGN9pNQQoY|fc_0f029265420fe376016a1bfe8f87d08191911de94c1e5d2173","name":"bash","arguments":{"command":"python - <<'PY'\nfrom bs4 import BeautifulSoup\nfrom pathlib import Path\nfor name in ['c05e51dd412952e4','98558fbdd89c6b67','c22ede05fdcde865','b5ddc6629f6a7c2d']:\n    html=Path(f'/var/www/rss-offline/article-{name}.html').read_text()\n    soup=BeautifulSoup(html,'html.parser')\n    print('\\nFILE',name)\n    for sel in ['main','article','[role=main]','.entry-content','.post-content','.MarkdownStyles__StyledMarkdown-sc-ee165f5-0','.Sidebar__StyledSidebarContent-sc-b4087769-5','.blog-slug__StyledPostContent-sc-3613c11f-6','main article','.wp-block-post-content','.blog-post__content']:\n        found=soup.select(sel)\n        if found:\n            txt=max(found,key=lambda n: len(n.get_text(' ',strip=True)))\n            print(sel,'count',len(found),'text',len(txt.get_text(' ',strip=True)))\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":63622,"output":297,"cacheRead":32256,"cacheWrite":0,"totalTokens":96175,"cost":{"input":0.159055,"output":0.004455,"cacheRead":0.008064,"cacheWrite":0,"total":0.17157399999999998}},"stopReason":"toolUse","timestamp":1780219531848,"responseId":"resp_0f029265420fe376016a1bfe8bf614819181931d28d291ded6"}}
{"type":"message","id":"44771c13","parentId":"f2e9b6be","timestamp":"2026-05-31T09:25:39.406Z","message":{"role":"toolResult","toolCallId":"call_oMj0pGLSGf9LppPGN9pNQQoY|fc_0f029265420fe376016a1bfe8f87d08191911de94c1e5d2173","toolName":"bash","content":[{"type":"text","text":"\nFILE c05e51dd412952e4\nmain count 1 text 4496\narticle count 6 text 240\nmain article count 6 text 240\n\nFILE 98558fbdd89c6b67\nmain count 1 text 13392\narticle count 6 text 304\nmain article count 6 text 304\n\nFILE c22ede05fdcde865\n.MarkdownStyles__StyledMarkdown-sc-ee165f5-0 count 1 text 4472\n.Sidebar__StyledSidebarContent-sc-b4087769-5 count 1 text 4879\n.blog-slug__StyledPostContent-sc-3613c11f-6 count 1 text 4879\n\nFILE b5ddc6629f6a7c2d\n.MarkdownStyles__StyledMarkdown-sc-ee165f5-0 count 1 text 10826\n.Sidebar__StyledSidebarContent-sc-b4087769-5 count 1 text 10902\n.blog-slug__StyledPostContent-sc-3613c11f-6 count 1 text 10902\n"}],"isError":false,"timestamp":1780219539406}}
{"type":"message","id":"15acfff7","parentId":"44771c13","timestamp":"2026-05-31T09:25:45.896Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Inspecting GitHub structure**\n\nI need to look into GitHub's main structure and its selectors. I'll write a script to print the top child tags and classes to help identify the article text selectors. This will give me a clearer view of how the content is organized. It's essential to get this right so I can work with the data effectively.","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bfe94683c81918e807bde8e5e4d1f\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_6XirCG88ifIRSXZp9GjRq9M3y-N1oAb0_vSAQflqBabLYNI_Fq_x6dXQRljnHEJLG-LbTTNPWqwIB9YQbhqYYxgOcprBb2AL2bf3cYQ4pkNqHVwoNko7hxT8rqvVjGsiFrRAGdopY3MAgzAXpMLXoNpGtoC6u3DTTOKD72ulfx4fzUjPuwx8OhkrgJGYTpNufK8FFrPBive9GsgtHIbT8QwpkCdiInq7DZIEMFpKig2ZkTalVR7d8NTu_tjdHUF51R91GsHkaKEwMC8B3Zu5BUPfA9R6UBxmm_Z3g9KHI8bKpzDEUkpf9AsObcAVhkjvMYNzAi0rWNsCTTKNcXrGq34J_6ZxzeWBgsBCJ0KBMzHGa2njqHz8sGsb47lPO3N4GbYQ3rcwbUFejo4K0RAAxRcY2y5z8g-Uuma29rKKT89awza3R83eoGf_ZoqfHRS9XKzQgrpOOX2uTdFWscHSnD1kxxIu-2O0c_wyplfTNWyCkTgBqpAQDRZZsMOsr8-5ljJGV-pcPS7Q3i3h4lwN7xmsC-kSpaS0UjD7zFxAtRfIS_w8tu7_HiGNKg_Ncl8FjkTebvPPZVJ5o2qklGw5Hovap9SoGjq27D2CH_7s95AMcgcPG_EEnuLsPvUDyHdSrOvjeHC1oA0CX6P9-O70SToWgGHFrVsn8LZy7j7fsMOEpkT3gsng02tY3HoX_837sbpxOIpRdB6jUsBvje0yBr8BznsM7oOyRJj1blmSQY8epOJ4laAx6RLgwvLKdrOLsY7FzsdO_2VEIKCf9JhqiRN4OzVIzywnhnnDt3Hdso3izrcaMz0fMpgWQFW7JaFUgSslSfNjMBTPz-plwcMwziLgu44pxbnAmhvfNC-vc7W7Z8Zbk3McdP3qORDBScvXjWHaLKTF3uDAGhSbqfmlmdG9mSF3YZCwR1XoDr7gRo_cjHnSoYnpkd_0cVgjvEEkuaF4IKWo2j7dWKx0IiPfeE_DlOUQjoxzvP5_ITuXo8fMrkabTfCUc1uqyH_fbWK7VUKINg26SL4Qoia8ALAnOqGfzr27KnfM2KsI0nSANmwhZUytZ0vL5VGZzgd3P-E3EOwn1Aw27g-CiZTRxKsiZj0Q==\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Inspecting GitHub structure**\\n\\nI need to look into GitHub's main structure and its selectors. I'll write a script to print the top child tags and classes to help identify the article text selectors. This will give me a clearer view of how the content is organized. It's essential to get this right so I can work with the data effectively.\"}]}"},{"type":"toolCall","id":"call_gRoZmcilfiJLsPWbF2FjhVep|fc_0f029265420fe376016a1bfe9705688191b9fb63ddf42f49f7","name":"bash","arguments":{"command":"python - <<'PY'\nfrom bs4 import BeautifulSoup\nfrom pathlib import Path\nfor name in ['c05e51dd412952e4','98558fbdd89c6b67']:\n    soup=BeautifulSoup(Path(f'/var/www/rss-offline/article-{name}.html').read_text(),'html.parser')\n    main=soup.find('main')\n    print('\\nFILE',name)\n    for child in main.find_all(recursive=False)[:12]:\n        print(child.name, child.get('id'), child.get('class'))\n    # best big descendants with class/id hints\n    cands=[]\n    for tag in main.find_all(True):\n        txt=len(tag.get_text(' ',strip=True))\n        if txt<300: continue\n        ident=' '.join(tag.get('class',[])) or tag.get('id') or ''\n        if ident:\n            cands.append((txt,tag.name,ident[:120]))\n    for txt,tag,ident in sorted(cands, reverse=True)[:20]:\n        print(txt, tag, ident)\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":675,"output":296,"cacheRead":95744,"cacheWrite":0,"totalTokens":96715,"cost":{"input":0.0016875000000000002,"output":0.00444,"cacheRead":0.023936,"cacheWrite":0,"total":0.0300635}},"stopReason":"toolUse","timestamp":1780219539407,"responseId":"resp_0f029265420fe376016a1bfe937bf08191b1ab793560a9ac70"}}
{"type":"message","id":"446741c9","parentId":"15acfff7","timestamp":"2026-05-31T09:25:46.236Z","message":{"role":"toolResult","toolCallId":"call_gRoZmcilfiJLsPWbF2FjhVep|fc_0f029265420fe376016a1bfe9705688191b9fb63ddf42f49f7","toolName":"bash","content":[{"type":"text","text":"\nFILE c05e51dd412952e4\nheader None ['position-relative']\nsection None ['container-xl', 'mx-auto', 'p-responsive-blog', 'mt-4']\ndiv None ['container-xl', 'mx-auto', 'p-responsive-blog', 'mt-4', 'mt-md-7', 'mb-7', 'mb-md-9']\nsection None ['container-xl', 'p-responsive-blog', 'mx-auto', 'mt-12', 'mb-8']\naside None ['container-xl', 'p-responsive-blog', 'mx-auto', 'mb-12']\ndiv None ['py-6', 'py-lg-8', 'color-bg-subtle']\n2720 div d-flex flex-wrap flex-justify-center gutter-spacious\n2720 div container-xl mx-auto p-responsive-blog mt-4 mt-md-7 mb-7 mb-md-9\n2293 section col-12 col-md-8 col-lg-7 post__content is-layout-constrained post-96339 post type-post status-publish format-standard ha\n624 section container-xl p-responsive-blog mx-auto mt-12 mb-8\n610 div post-columns post-columns--3-3\n477 aside container-xl p-responsive-blog mx-auto mb-12\n452 div d-flex flex-wrap ml-md-n4\n426 aside col-12 col-md-4 col-lg-3 post__sidebar\n414 div position-sticky top-md-scroll\n374 section newsletter rounded-2\n374 div py-6 py-lg-8 color-bg-subtle\n374 div d-flex flex-row flex-wrap gutter-spacious py-5 px-4 py-md-6 px-md-5\n374 div container-xl p-responsive-blog\n374 div color-bg-subtle rounded-2\n\nFILE 98558fbdd89c6b67\nheader None ['position-relative']\nsection None ['container-xl', 'mx-auto', 'p-responsive-blog', 'mt-4']\ndiv None ['container-xl', 'mx-auto', 'p-responsive-blog', 'mt-4', 'mt-md-7', 'mb-7', 'mb-md-9']\nsection None ['container-xl', 'p-responsive-blog', 'mx-auto', 'mt-12', 'mb-8']\naside None ['container-xl', 'p-responsive-blog', 'mx-auto', 'mb-12']\ndiv None ['py-6', 'py-lg-8', 'color-bg-subtle']\n11715 div d-flex flex-wrap flex-justify-center gutter-spacious\n11715 div container-xl mx-auto p-responsive-blog mt-4 mt-md-7 mb-7 mb-md-9\n11057 section col-12 col-md-8 col-lg-7 post__content is-layout-constrained post-96273 post type-post status-publish format-standard ha\n727 ol wp-block-list\n657 aside col-12 col-md-4 col-lg-3 post__sidebar\n617 div position-sticky top-md-scroll\n584 section container-xl p-responsive-blog mx-auto mt-12 mb-8\n570 div post-columns post-columns--3-3\n477 aside container-xl p-responsive-blog mx-auto mb-12\n452 div d-flex flex-wrap ml-md-n4\n401 ol wp-block-list\n395 section mb-7 mb-md-8 mt-7 mt-md-0\n374 section newsletter rounded-2\n374 div py-6 py-lg-8 color-bg-subtle\n374 div d-flex flex-row flex-wrap gutter-spacious py-5 px-4 py-md-6 px-md-5\n374 div container-xl p-responsive-blog\n374 div color-bg-subtle rounded-2\n318 ol wp-block-list\n315 div mt-8 mb-8 mb-md-0\n304 div author-bio__content\n"}],"isError":false,"timestamp":1780219546236}}
{"type":"message","id":"327d5f30","parentId":"446741c9","timestamp":"2026-05-31T09:26:06.695Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Implementing site-specific targeting**\n\nI need to work on site-specific targeting for the make_pdf_printable_copy function, possibly before isolating by fragment. I could select the main content container based on the domain, and I should add a function called extract_print_root. For instance, on GitHub, I would select section.post__content, while for DigitalOcean, I might go for the class that contains MarkdownStyles__StyledMarkdown. It’s important to have robust substring-based selectors and to remove headers or navigation before printing. Let's refine this!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bfe9b8b248191a0a8e27d524c8dcb\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_6ed1tpBc5KAgQd_BWh6yCY6vaIfiAtlgH2TywY0ANKeuC6PlcsfBCR7I1R0BUHixDcqNuk3NYmD8-WWNKtc1SAmPSiYLXt1NWxBXQxnzmRAd4yCEDjVnz8LV9kTaPma6ecM7dMMoHrUWXx2XC7M0fmHsWCwsypfw3D_zJenzJwIq26Hq4kygT28WMduAPZCO88-muXgT25RjkMs20qCNfvNr0FB5O6Q2qe6PN4tHqF2Z_Stx0FXCRBj8B2kwEzfJOAuki-mc575RIuw0q6lpKtrmSWy9hMI7YTyG5Xv3tJH1fb28TcAsHz5m78qoN4kexi5k_cB1hOMNWtvvrB6CRUyTMfe_VPbWmqUt1DgVRp7wz2UW0LHDtY8f9TCq2xqX32QQQ4roC0vHA4heVQtA7bqAS1jI-eHPhRbouiMqMDMclt8i8nvhRprSHEj6iXvmw7f5RFkypKGWPM6ADXBAfY57zno_LXbd7BvXpKWVbv6PIKUnXKp5n0JGMiNQ21sSPCJq5pqE8urhVHYAkAGdfnV1iZZ3t4oN12Bo7RrcsTau-5a0QR7LjbNzJY0qxQYyHHaErGAbmy8D9x0UfeP9hoB3_ZgVtxbnjC_97sy58GvgfX8bZjn8NbvQlaMrOv3DeYEpZYfQ7tpvs00tLakx56uHXxt_nK8k0udCN5jUA5knBTncFHJkElp4yiSqRDjG5A4px9Q2iPM7mPb2hEMzzX7KVSNrfHQSnh3_KBJNb7iSAsJlMbBmfRT5UHyvz6YuG3T4DP_7ebhsm5Y2o2BPeOpZpCGcbYclxILcHDiqrXAXN87NE7ZSGVKOu8DJW034hHwhvjFSPWp4youB9kXEerrivDtCn0Bd84yWqkoel0cf7NOfrPScXsDk51Jgt6Chtr5hCo4LkoaNU74THHKK8j1yFEtvfbHf9tXngUIrEv8zs9QzqyNUhUEyKgBY2Z20kobnyjqvn4XxDrDCTZ6aQQ8VLUqSbFAN5NbKCkbZPo9f-99FNkbP8b4hAZV2yMgNZ5ecaY1b_Je-TRqgw37XpchaBs087MkMmUFWLalpD8ql5t9IIHL_1hXuqRLtlTNRzyNMQuAxPMs6jH0z-l7GLvS8J6aCRR3-oV1jtyFGTz-vgRemt6pnsbcMSWHu3HVQ8fbnBoU4ybjrYbYeSDfAn-1Fjq6AeYV7oApO5IUUphPB9EkMAtnub9itdiSOAatN4-Df1MQRy3n4lzx4R_8p0ThQTEVur_pU5pLdPHE6Zdn51__j31_2FicVJkSeKfS-UfdXImXch2VcT7kplSf1L7ceeDiacyUwPlVai9Ro4BwjbI6dh0J91qhlJAqqyswcCdy53LH_ChsmL1_i0D1cOgUWuYlpa0GRJ4-KDgkLCR2Os5Zty3bb32RxsS6YNrIeygaohi0tZFy0BVtXGRnnK9mos0LMHyy1nSyBOukXoUhC2L7HQgRPGfT6TKOuuWIXZ9Z5bObG19qwVsjjRBW-zV3i8MLTxYMxlth69587PFe0mz-RMa_pEpYxRlg6FmkIDu53hbmqkZvDClma2hpjfc-02uLurw-c1RVZeJQL5AZjVR80mKjN7t4eJxFOIyxP_2z6y0LEBYPwEtJSuIWT6hiJPvOgWP1Ab3DL7S8u7B6Zw=\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Implementing site-specific targeting**\\n\\nI need to work on site-specific targeting for the make_pdf_printable_copy function, possibly before isolating by fragment. I could select the main content container based on the domain, and I should add a function called extract_print_root. For instance, on GitHub, I would select section.post__content, while for DigitalOcean, I might go for the class that contains MarkdownStyles__StyledMarkdown. It’s important to have robust substring-based selectors and to remove headers or navigation before printing. Let's refine this!\"}]}"},{"type":"toolCall","id":"call_qt4WLrQuBJzq3Pc4KuWkSsxe|fc_0f029265420fe376016a1bfe9ee2b88191862586c804aea2a7","name":"edit","arguments":{"path":"/home/sebas/work/projects/rss-offline/build.py","edits":[{"oldText":"def isolate_anchor_section(soup: BeautifulSoup, entry: dict):\n    parsed = urlparse(entry['link'])\n    fragment = (parsed.fragment or '').strip()\n    if not fragment:\n        return soup\n    target = soup.find(id=fragment)\n    if target is None:\n        target = soup.find(attrs={'name': fragment})\n    if target is None:\n        return soup\n    container = target\n    for _ in range(6):\n        parent = container.parent\n        if parent is None or not getattr(parent, 'name', None):\n            break\n        text_len = len(parent.get_text(' ', strip=True))\n        if 200 < text_len < 20000:\n            container = parent\n            break\n        container = parent\n    wrapper = BeautifulSoup('<!doctype html><html><head></head><body></body></html>', 'html.parser')\n    wrapper.body.append(BeautifulSoup(str(container), 'html.parser'))\n    return wrapper\n\n\ndef clean_pdf_soup(soup: BeautifulSoup):\n","newText":"def isolate_anchor_section(soup: BeautifulSoup, entry: dict):\n    parsed = urlparse(entry['link'])\n    fragment = (parsed.fragment or '').strip()\n    if not fragment:\n        return soup\n    target = soup.find(id=fragment)\n    if target is None:\n        target = soup.find(attrs={'name': fragment})\n    if target is None:\n        return soup\n    container = target\n    for _ in range(6):\n        parent = container.parent\n        if parent is None or not getattr(parent, 'name', None):\n            break\n        text_len = len(parent.get_text(' ', strip=True))\n        if 200 < text_len < 20000:\n            container = parent\n            break\n        container = parent\n    wrapper = BeautifulSoup('<!doctype html><html><head></head><body></body></html>', 'html.parser')\n    wrapper.body.append(BeautifulSoup(str(container), 'html.parser'))\n    return wrapper\n\n\ndef pick_print_root(soup: BeautifulSoup, entry: dict):\n    host = urlparse(entry['link']).netloc.lower()\n    selectors = []\n    if host.endswith('github.blog'):\n        selectors = [\n            'section.post__content',\n            '[class*=\"post__content\"]',\n            'main .post__content',\n        ]\n    elif host.endswith('digitalocean.com'):\n        selectors = [\n            '[class*=\"MarkdownStyles__StyledMarkdown\"]',\n            '[class*=\"blog-slug__StyledPost\"]',\n            '[class*=\"Sidebar__StyledSidebarContent\"]',\n        ]\n\n    for selector in selectors:\n        nodes = soup.select(selector)\n        if not nodes:\n            continue\n        best = max(nodes, key=lambda n: len(n.get_text(' ', strip=True)))\n        if len(best.get_text(' ', strip=True)) >= 300:\n            wrapper = BeautifulSoup('<!doctype html><html><head></head><body></body></html>', 'html.parser')\n            wrapper.body.append(BeautifulSoup(str(best), 'html.parser'))\n            return wrapper\n    return soup\n\n\ndef clean_pdf_soup(soup: BeautifulSoup):\n"},{"oldText":"    soup = BeautifulSoup(src_path.read_text(), 'html.parser')\n    soup = isolate_anchor_section(soup, entry)\n    soup = clean_pdf_soup(soup)\n","newText":"    soup = BeautifulSoup(src_path.read_text(), 'html.parser')\n    soup = pick_print_root(soup, entry)\n    soup = isolate_anchor_section(soup, entry)\n    soup = clean_pdf_soup(soup)\n"}]}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1238,"output":980,"cacheRead":96256,"cacheWrite":0,"totalTokens":98474,"cost":{"input":0.003095,"output":0.0147,"cacheRead":0.024064,"cacheWrite":0,"total":0.04185899999999999}},"stopReason":"toolUse","timestamp":1780219546238,"responseId":"resp_0f029265420fe376016a1bfe9a4ec48191b7bbfaba03d65a4d"}}
{"type":"message","id":"99cd2df4","parentId":"327d5f30","timestamp":"2026-05-31T09:26:06.738Z","message":{"role":"toolResult","toolCallId":"call_qt4WLrQuBJzq3Pc4KuWkSsxe|fc_0f029265420fe376016a1bfe9ee2b88191862586c804aea2a7","toolName":"edit","content":[{"type":"text","text":"Successfully replaced 2 block(s) in /home/sebas/work/projects/rss-offline/build.py."}],"details":{"diff":"     ...\n 298     wrapper.body.append(BeautifulSoup(str(container), 'html.parser'))\n 299     return wrapper\n 300 \n 301 \n+302 def pick_print_root(soup: BeautifulSoup, entry: dict):\n+303     host = urlparse(entry['link']).netloc.lower()\n+304     selectors = []\n+305     if host.endswith('github.blog'):\n+306         selectors = [\n+307             'section.post__content',\n+308             '[class*=\"post__content\"]',\n+309             'main .post__content',\n+310         ]\n+311     elif host.endswith('digitalocean.com'):\n+312         selectors = [\n+313             '[class*=\"MarkdownStyles__StyledMarkdown\"]',\n+314             '[class*=\"blog-slug__StyledPost\"]',\n+315             '[class*=\"Sidebar__StyledSidebarContent\"]',\n+316         ]\n+317 \n+318     for selector in selectors:\n+319         nodes = soup.select(selector)\n+320         if not nodes:\n+321             continue\n+322         best = max(nodes, key=lambda n: len(n.get_text(' ', strip=True)))\n+323         if len(best.get_text(' ', strip=True)) >= 300:\n+324             wrapper = BeautifulSoup('<!doctype html><html><head></head><body></body></html>', 'html.parser')\n+325             wrapper.body.append(BeautifulSoup(str(best), 'html.parser'))\n+326             return wrapper\n+327     return soup\n+328 \n+329 \n 302 def clean_pdf_soup(soup: BeautifulSoup):\n 303     selectors = [\n 304         'script', 'noscript', 'iframe', 'video', 'audio',\n 305         '[aria-modal=\"true\"]', '[role=\"dialog\"]',\n     ...\n 326     pdf_dir = OUTPUT_DIR / PDF_DIRNAME\n 327     pdf_dir.mkdir(parents=True, exist_ok=True)\n 328     printable_path = pdf_dir / f'article-{slug(entry[\"link\"])}.print.html'\n 329     soup = BeautifulSoup(src_path.read_text(), 'html.parser')\n+358     soup = pick_print_root(soup, entry)\n 330     soup = isolate_anchor_section(soup, entry)\n 331     soup = clean_pdf_soup(soup)\n 332     html = str(soup)\n 333     html = html.replace('/rss-offline/', 'file:///var/www/rss-offline/')\n     ...","patch":"--- /home/sebas/work/projects/rss-offline/build.py\n+++ /home/sebas/work/projects/rss-offline/build.py\n@@ -298,8 +298,36 @@\n     wrapper.body.append(BeautifulSoup(str(container), 'html.parser'))\n     return wrapper\n \n \n+def pick_print_root(soup: BeautifulSoup, entry: dict):\n+    host = urlparse(entry['link']).netloc.lower()\n+    selectors = []\n+    if host.endswith('github.blog'):\n+        selectors = [\n+            'section.post__content',\n+            '[class*=\"post__content\"]',\n+            'main .post__content',\n+        ]\n+    elif host.endswith('digitalocean.com'):\n+        selectors = [\n+            '[class*=\"MarkdownStyles__StyledMarkdown\"]',\n+            '[class*=\"blog-slug__StyledPost\"]',\n+            '[class*=\"Sidebar__StyledSidebarContent\"]',\n+        ]\n+\n+    for selector in selectors:\n+        nodes = soup.select(selector)\n+        if not nodes:\n+            continue\n+        best = max(nodes, key=lambda n: len(n.get_text(' ', strip=True)))\n+        if len(best.get_text(' ', strip=True)) >= 300:\n+            wrapper = BeautifulSoup('<!doctype html><html><head></head><body></body></html>', 'html.parser')\n+            wrapper.body.append(BeautifulSoup(str(best), 'html.parser'))\n+            return wrapper\n+    return soup\n+\n+\n def clean_pdf_soup(soup: BeautifulSoup):\n     selectors = [\n         'script', 'noscript', 'iframe', 'video', 'audio',\n         '[aria-modal=\"true\"]', '[role=\"dialog\"]',\n@@ -326,8 +354,9 @@\n     pdf_dir = OUTPUT_DIR / PDF_DIRNAME\n     pdf_dir.mkdir(parents=True, exist_ok=True)\n     printable_path = pdf_dir / f'article-{slug(entry[\"link\"])}.print.html'\n     soup = BeautifulSoup(src_path.read_text(), 'html.parser')\n+    soup = pick_print_root(soup, entry)\n     soup = isolate_anchor_section(soup, entry)\n     soup = clean_pdf_soup(soup)\n     html = str(soup)\n     html = html.replace('/rss-offline/', 'file:///var/www/rss-offline/')\n","firstChangedLine":302},"isError":false,"timestamp":1780219566738}}
{"type":"message","id":"1da9a164","parentId":"99cd2df4","timestamp":"2026-05-31T09:26:11.379Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Verifying syntax**\n\nI think it's a good idea to check my syntax to ensure everything is correct. It’s so easy to overlook small errors that might cause bigger issues down the line. I should run through the code and spot any mistakes. Maybe I’ll also look into best practices for syntax to enhance what I’m working on. Knowing that a little adjustment can make a big difference keeps me motivated to double-check!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bfeb05efc8191b8430b2c624d8e88\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_6zhbhVa-4n9p1hvejAeEg6BVwKZxHyJLR57Uw8RcOKOCgetsr1x5APvue0SIzv2JMmzRirVxOo0p1GnUFmdoi_nreN38yznCRCB7OdbnAfi39iZ3qiytXj8Ufp7BDMYbYC6UJHkc8VL-Ru6BZWIzAN-KNSpGr3oW9zVTIctVosRQ8qT0ucd7XNv8yTkkG59oXQ6koAcoOGDhzwrW1ARTezyZz0Hf1wM22vBXKaNG1NW9W3-3R6I18N9d_4Gf_VuNVgAhKYFqpE-6XRSjXFXcTsqdXJNwyak3RBQHRilYOwq4dJmEtDqK6nN5aGwLEhijmjMdyxp9pM-GQ33JCR8RTUsE1nQcA01qG2ZGNypLtRvBmjmoyGsI4PuGbItS6OjLMFP5lG2faFUe8Wese9BrZEjD1BU6xZb6mbGORkaLQ-w5Ajpnl0_tLBSLELvUl19rfFSnPlhgupwoilqLFl4P3OktSiLOeY3JqMHRRs10T8WO_sGG2EMvRTr_nvC4j9mwkWmm142w5_y6gbY2r3R7NXBZNHCoauBqi5Peln3rrwBT4o5OVN5J04RQc_UHIPLnqi1et3yRNhyNOdjYuE5hdswpBcYenAkxbyRLKK1HeSQy9LL1QDBLXSZ0iBRVtxVWI8FtBTuNFbQnbwQJb2ZE-qUu0DXcHTG_O94d64DLYdwUGFf06Tz4vXZ2XnAi7KmJp9ooUpiaLa-QJCOly51qEc8ZYpxGTdrV9slCDUnKXAijdpWrcA4rkK2aKkyGkh8PF4qJcUT7CM25j8z0ez_N9oSag4kdSScvzRTGQ_fpma8M2U33_4gtKtPhRdeWHn9-Q8VRVfXL5z39aiYNe6HN5ozZIJy4DI0kI4Z_o2LkobjAqZEGmCPvqhC12DOTOs3851buaiglRWvqekxgB2j_dWoJfEhIwMXSius2Z48Dwk4Bsiy0dfH8bWh_ByHxMGk0r7\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Verifying syntax**\\n\\nI think it's a good idea to check my syntax to ensure everything is correct. It’s so easy to overlook small errors that might cause bigger issues down the line. I should run through the code and spot any mistakes. Maybe I’ll also look into best practices for syntax to enhance what I’m working on. Knowing that a little adjustment can make a big difference keeps me motivated to double-check!\"}]}"},{"type":"toolCall","id":"call_vN7IB1Yl3HZG3RbeEWLRY5Xy|fc_0f029265420fe376016a1bfeb303188191904c656f0b7ff1f1","name":"bash","arguments":{"command":"python -m py_compile /home/sebas/work/projects/rss-offline/build.py && python - <<'PY'\nfrom pathlib import Path\nimport importlib.util\nspec=importlib.util.spec_from_file_location('rssbuild','/home/sebas/work/projects/rss-offline/build.py')\nmod=importlib.util.module_from_spec(spec)\nspec.loader.exec_module(mod)\nprint('loaded ok')\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1224,"output":119,"cacheRead":97280,"cacheWrite":0,"totalTokens":98623,"cost":{"input":0.0030600000000000002,"output":0.0017850000000000001,"cacheRead":0.024319999999999998,"cacheWrite":0,"total":0.029164999999999996}},"stopReason":"toolUse","timestamp":1780219566741,"responseId":"resp_0f029265420fe376016a1bfeaece90819197085fe49d7b39e0"}}
{"type":"message","id":"322dc742","parentId":"1da9a164","timestamp":"2026-05-31T09:26:11.728Z","message":{"role":"toolResult","toolCallId":"call_vN7IB1Yl3HZG3RbeEWLRY5Xy|fc_0f029265420fe376016a1bfeb303188191904c656f0b7ff1f1","toolName":"bash","content":[{"type":"text","text":"loaded ok\n"}],"isError":false,"timestamp":1780219571728}}
{"type":"message","id":"cb59245b","parentId":"322dc742","timestamp":"2026-05-31T09:26:14.393Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bfeb4c8ec819181d60529c1552701\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_62XHpZUuhb5vlOJh3Y3x-w_pu54pkZv45uzivTyaLV0rHsokVXujQApV18Nk-mnjPBx6cDuiyPD9ojqzESHRq4TjMsAqCOh4Ee_QbcGVNwEnPMmDLw3_nw77UIM3hWicjFY5SdXVKNeh0dg3zV_ykk7X4OVSb1b2COXhzZLyxzJs_PHPqA4gbJK_M0UW8HWPu1Qnpd7fC-FtRIkROUsJhfyZTykyW6NcmtXxSz74mLGMf3FbEhDL5IjLS7Ypo227JgXhMGemAX0C6RAorLf2dV96nEWlztCRBLnj6oDdCCQnZwXeGexgVszNH36X0VuG9ijVBh8Ko69TbzA6HWo-3B1cog1nqyDVbUxdgUmcoZkIc9oTTkT28PwHYvh87sAHFfQGuGT6TiJTNvAqhwMC-ZOFh3jO47BpYajrq6cW-s17jgnmvfi3X_EGq9UFFDC7d_hzKZdc6J43sefFRVcvhlK54bxFWrfthKYvr3vdioox_4497e5IaUc2AmHvNzNc-lSIapI0YbLCCtggjAgBkPjAfbm__CDln3LvAcBov_Z4Wz5ZfD-c7pbFhLUfQh3YMXLkD1jv-STX_jXLKuGXMHIFlqHyB8RzEfXotnYrgQYzrRjmaCDPu146A5dSwi8f_GzJb0c_AfJ-ffHYfWirdXeTLmUVPoPDdyDVoBtyQtGsB8pJu1N8rGxKCi3RL0I8EYZmyMttSQSh8JuOAOKyEXRijlxfrNbsiWXb72RbdxO03sllFsQi8Klc3pbae9kZ2Avi6RqI3_394JVwTvEme0UvMy0Cz0OW9pBJWTs0a0aP123wkPUvFMkaIGimZR3u5p3GSYYrnbJv6876h-zRYWIl5dGnshiyRTO4onSYzkruq0iHnBepUjBZ4e9loJV76smqfgiF4yVB9BlceyS7uuk_6VTz6MzNdscaaAfxizA9u0ymsH3vJxrlOfFG7q-woP\",\"summary\":[]}"},{"type":"toolCall","id":"call_qIblLg2aW5vHUAhVRm1bbFSZ|fc_0f029265420fe376016a1bfeb630f481918d69db67edd0b8d8","name":"todo","arguments":{"action":"update","id":3,"status":"completed"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":333,"output":35,"cacheRead":98304,"cacheWrite":0,"totalTokens":98672,"cost":{"input":0.0008325,"output":0.000525,"cacheRead":0.024576,"cacheWrite":0,"total":0.0259335}},"stopReason":"toolUse","timestamp":1780219571730,"responseId":"resp_0f029265420fe376016a1bfeb3caa08191ac34151cb40edb59"}}
{"type":"message","id":"2e04c072","parentId":"cb59245b","timestamp":"2026-05-31T09:26:14.394Z","message":{"role":"toolResult","toolCallId":"call_qIblLg2aW5vHUAhVRm1bbFSZ|fc_0f029265420fe376016a1bfeb630f481918d69db67edd0b8d8","toolName":"todo","content":[{"type":"text","text":"Updated #3 (pending → completed)"}],"details":{"action":"update","params":{"action":"update","id":3,"status":"completed"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"completed","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"},{"id":3,"subject":"Tighten rss-offline PDF extraction for noisy blogs","status":"completed","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","owner":"agent"}],"nextId":4},"isError":false,"timestamp":1780219574394}}
{"type":"message","id":"eec21d4f","parentId":"2e04c072","timestamp":"2026-05-31T09:26:20.618Z","message":{"role":"assistant","content":[{"type":"text","text":"Sí. Ya dejé el builder más apuntado.\nNo rebuildeé.\n\nQué vi:\n- GitHub Blog:\n  - el print estaba agarrando casi todo `main`\n  - eso mete related/newsletter/sidebar/chrome\n  - selector bueno:\n    - `section.post__content`\n- DigitalOcean:\n  - el artículo real ya está bien aislado más abajo\n  - pero el print seguía arrastrando header/topbar/sidebar en varios casos\n  - selector bueno:\n    - `[class*=\"MarkdownStyles__StyledMarkdown\"]`\n  - fallback útil:\n    - `[class*=\"blog-slug__StyledPost\"]`\n    - `[class*=\"Sidebar__StyledSidebarContent\"]`\n\nCambio en builder:\n- agregué `pick_print_root(...)`\n- hace targeting por host:\n  - `github.blog` → `section.post__content`\n  - `digitalocean.com` → markdown/article container\n- después de eso sigue:\n  - `isolate_anchor_section(...)`\n  - `clean_pdf_soup(...)`\n\nImpacto esperado:\n- PDFs de GitHub:\n  - bastante menos basura\n  - más foco en cuerpo del post\n- PDFs de DigitalOcean:\n  - menos header/nav/sidebar\n  - más limpio y corto\n\nNo hice rebuild.\nVa a aplicar en el próximo.","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1bfeb790488191a132fd69e58ccbb1\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":386,"output":275,"cacheRead":98304,"cacheWrite":0,"totalTokens":98965,"cost":{"input":0.000965,"output":0.004125,"cacheRead":0.024576,"cacheWrite":0,"total":0.029666}},"stopReason":"stop","timestamp":1780219574396,"responseId":"resp_0f029265420fe376016a1bfeb67b2081918d7c739bfef81e28"}}
{"type":"message","id":"f05333fa","parentId":"eec21d4f","timestamp":"2026-05-31T09:29:23.921Z","message":{"role":"user","content":[{"type":"text","text":"[telegram] otros feeds viste? quiero enfocar todos"}],"timestamp":1780219763918}}
{"type":"message","id":"08826e20","parentId":"f05333fa","timestamp":"2026-05-31T09:29:30.542Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Suggesting concise answers**\n\nI want to keep my answers concise. I’m considering whether I should mention something I've checked recently, as it seems like the user only needs that information. Maybe I should even suggest a broader perspective just to ensure they have all the options. It feels like a good approach to balance clarity with depth, but I’m weighing how much detail is really needed. Let me think this through as I finalize my response!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bff767e0481919c8150a8cbef7723\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG_94qAe_T7pbByhyfw2utYmAaSJQlIRnY1gGnBlBCwU0HDkg6n_xytvOS0Pll3-6oWwEWXdU3Y1aTHCYGu7qU0x9b6hBUnfIq7GNn_mnivm8s_RZIRh5yLsMiwQV-TsPZla51qk59plN7PuaJatSrEJzQ6g8KaUWEKe-9W1MOLwrLsCyuQLOFaC8YmpLgZwGT0DVdFP-s9N9VwE6QGv3GFPZJzE4BYGg_WwercmB0X5r56GoWp8xcWudkdf99AG4OWNXbnztn7XvfmBFj_EkP4a0cqg3yg0UbYu0DvP6ZV2pgLM8s0JuMoeSG43lR_7e9qwtuT3etDJYdTzamPV1HWc3o5m58BW_ohnhFH_lVzc43vPQh_1NT4seyCYly49gAGy_xoejVyg2nTsYlVDwvj7P9_Fmucq1-60H-1K6hkDrVCW9bn06GBDn04Ka4rpIRX7TBtQL75-mx177-t4yoztL03EPQCf1atg5UeG6tDTcpuMvFvI4pKLxgoT5Djh8GpY8Wuq5yLC_oPzPinaCVqRARV6j2QXV_x5BD5E1DT2JEJYPAHLs3alPPJHQUMFxoNWDN1Q9n7X7d8FTwXnM2EQItLLju84hjN9m3W1rJyFumOj6itSMLd7qIYuV5h5w3pqXerlzz-eISKw6_Kv4EP_rwsDNXoICs-x8O2GH5YuOPxgwo8oHfCA_z-S0-EfY7fSFEe-Y6aXl0_xaofYUyi6fNsMUJQyR757y2hbfuT0zWyVHvtNsn5IhjWf8VfqvcZWX9RRAtzhQJwVTt5UyTvs6HGtnpCuVpgpjPV8AbZcWumhF7hihaT7RrGj_rQ2rZ1mZM_59PwbuXcpYkPPKiqxSdRZkYqAV7qadU0lOdCPltN5D6sOZ8ThfdapWc7SkUVD-OxTUyNE3km2o8ROXKMnJAy_v27_GtzYPRxZWaVU5kSKIi_k3ZApDYHWPOjIQGm8YYhjyqKLvpozyy4C1_92S34Rie9e4bE-IDSGpRW-5y36uEepoDaNgzap3n9gFzFeO19Z7IU1w1t24MhLMD0MbVQ==\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Suggesting concise answers**\\n\\nI want to keep my answers concise. I’m considering whether I should mention something I've checked recently, as it seems like the user only needs that information. Maybe I should even suggest a broader perspective just to ensure they have all the options. It feels like a good approach to balance clarity with depth, but I’m weighing how much detail is really needed. Let me think this through as I finalize my response!\"}]}"},{"type":"text","text":"Sí, vi algo más, pero los peores eran esos dos.\n\nEstado:\n- `github.blog`\n  - claro patrón propio\n  - ya targeteado\n- `digitalocean.com/blog`\n  - claro patrón propio\n  - ya targeteado\n- otros recientes\n  - varios salen bastante bien con el flujo actual\n  - no vi todavía otro con tanta basura sistemática\n\nImportante:\n- ahora **no están enfocados todos**\n- están enfocados:\n  - GitHub Blog\n  - DigitalOcean Blog\n- el resto sigue con heurística genérica\n\nSi querés hacerlos todos bien, siguiente paso correcto:\n1. sacar hosts únicos de `entries.json`\n2. agrupar por dominio\n3. inspeccionar 1–2 ejemplos por dominio\n4. agregar selectors por host al builder\n\nEso sí te deja “todos” bastante más controlados.  \nSi querés, hago esa pasada ahora.","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1bff7820f88191a7f1754a52529e23\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":67986,"output":219,"cacheRead":30720,"cacheWrite":0,"totalTokens":98925,"cost":{"input":0.169965,"output":0.003285,"cacheRead":0.007679999999999999,"cacheWrite":0,"total":0.18093}},"stopReason":"stop","timestamp":1780219763926,"responseId":"resp_0f029265420fe376016a1bff7401188191846fbfc9b6cb5f03"}}
{"type":"message","id":"db8ef485","parentId":"08826e20","timestamp":"2026-05-31T09:31:16.732Z","message":{"role":"user","content":[{"type":"text","text":"[telegram] dale, hacelo a tu manera. no dejes basura despues eh, limpia si generas algo temporal"}],"timestamp":1780219876731}}
{"type":"message","id":"e7fa1f04","parentId":"db8ef485","timestamp":"2026-05-31T09:31:20.616Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Evaluating tasks for inspection**\n\nI need to inspect the entries of the hosts and consider patching the selectors, but I might not need to do a full rebuild. I'm thinking about whether I should update my to-do list with this task. It's important to make sure everything is in order, so I want to be thorough yet efficient in my approach. Let's see if there's a straightforward way to handle this!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bffe64ce48191b7445ac30d5e4791\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG__oj5EEjkpC-ybxkYBfsUEF1-otUnB5aU8e8QeduGOfHfwul6e9uwfQx_POq21MX4ypcqjj40lf49wHntPfB8-GBBW_NlwLb5FRz8hb7YhvH8HJx4Dm9iPIKBukOUM8aCkrxwuv7vyAQ_k4u33CrEgtF5Y3PiwEsyU8suLz5GD1tm8Z1ooHdfFcDTXXHq7SWORLpQ_FO7Efh8qf2f_e6LELRDyoeIymMKksyM84MdvxHV2z3a9POlUpZ4SitJpm8fEvrEXhT-r-1dATCsUDdxaDIldWWDNsm3U0fqKQY7QCm411p3wSSTVh88cKP0u8CK-pnAzNx_5QdJXvbnRru_f0E5F6G9T_WA98v7BxR6rvAzukFO2OTqzFYKaMcomwy3-rAtBI_Xk7CjJ1kZ8W62PvKMgw3RzptHf-ZrSQn-EDKa3eLOl3rR4yDQDVlC9Pc9lQBN8HcY4QxlVrvENzhDMrhjVO2HIC6ZXy_q6kTn3ZyTKqGUFg8m5_Ua5i-_c_QxKFR1qdNcQYSb3O0a-18EWrVw2E3Gpfxog0-nZlwDk22PDtl6fk5jBBXnAtni9D9XwDx7iJmuBhoKhNk5NQddUzaJZIdoGYXnpmkowy9qqeq3kfz6MUugPo-fa_ga7y5YUVdDC02beoN873m8jlFSLcXUp-otaR7_e9IYDMUJAULq8MUwlJHSNsvV-a-gv2VFG2wKPYxfew42YVdLSxgC1N4Ed4dN5QVhVWnHqKXUXL12O6ZAf7YLaJAgg-k5AaUb51-MwaS84qwwFtE-jpG-DkWHRPOR26TcXH9eLw3clAWuQ0Hgc_rOvJyRKNVdMtPgelvwDR2H7vmb4F5QBI6KHmYRLO4eyA1Mx8K4p4hzB_YV5OrZCvVEBqTBprH9r2BqnAxiQRt518UVUWKSZmV--K4RPRJZGW4171QM3sLck3UEm9xlDqfev_0fZ686RGEARzNRP19pjMRni_IKCDnVmiDPE84TyvcqKhnmlJDdI_z6wQi4pWUYot4IOf2GSpkgXyTshltCtKu68FEk6WBr5REQ==\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Evaluating tasks for inspection**\\n\\nI need to inspect the entries of the hosts and consider patching the selectors, but I might not need to do a full rebuild. I'm thinking about whether I should update my to-do list with this task. It's important to make sure everything is in order, so I want to be thorough yet efficient in my approach. Let's see if there's a straightforward way to handle this!\"}]}"},{"type":"toolCall","id":"call_WnDZenQxIUGgn6sov0FFQFFR|fc_0f029265420fe376016a1bffe83104819187f9a162e109538f","name":"todo","arguments":{"action":"create","subject":"Audit all recent rss-offline feed domains for PDF targeting","description":"Inspect current entries by domain, identify article-body selectors per host where useful, extend builder host-specific print root targeting, and clean any temporary artifacts. No rebuild unless needed for validation-only local parsing.","activeForm":"auditing rss-offline domains","status":"in_progress","owner":"agent"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":629,"output":114,"cacheRead":98304,"cacheWrite":0,"totalTokens":99047,"cost":{"input":0.0015725000000000001,"output":0.0017100000000000001,"cacheRead":0.024576,"cacheWrite":0,"total":0.0278585}},"stopReason":"toolUse","timestamp":1780219876734,"responseId":"resp_0f029265420fe376016a1bffe4cf148191afd3a1188f6f36fe"}}
{"type":"message","id":"1badbe77","parentId":"e7fa1f04","timestamp":"2026-05-31T09:31:20.617Z","message":{"role":"toolResult","toolCallId":"call_WnDZenQxIUGgn6sov0FFQFFR|fc_0f029265420fe376016a1bffe83104819187f9a162e109538f","toolName":"todo","content":[{"type":"text","text":"Created #4: Audit all recent rss-offline feed domains for PDF targeting (pending)"}],"details":{"action":"create","params":{"action":"create","subject":"Audit all recent rss-offline feed domains for PDF targeting","description":"Inspect current entries by domain, identify article-body selectors per host where useful, extend builder host-specific print root targeting, and clean any temporary artifacts. No rebuild unless needed for validation-only local parsing.","activeForm":"auditing rss-offline domains","status":"in_progress","owner":"agent"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"completed","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"},{"id":3,"subject":"Tighten rss-offline PDF extraction for noisy blogs","status":"completed","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","owner":"agent"},{"id":4,"subject":"Audit all recent rss-offline feed domains for PDF targeting","status":"pending","description":"Inspect current entries by domain, identify article-body selectors per host where useful, extend builder host-specific print root targeting, and clean any temporary artifacts. No rebuild unless needed for validation-only local parsing.","activeForm":"auditing rss-offline domains","owner":"agent"}],"nextId":5},"isError":false,"timestamp":1780219880617}}
{"type":"message","id":"6ad41fff","parentId":"1badbe77","timestamp":"2026-05-31T09:31:28.893Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_yiR2IjYhqTCSBho7pMD1i1lB|fc_0f029265420fe376016a1bfff0d6508191b3967d2ab9b44e3a","name":"bash","arguments":{"command":"python - <<'PY'\nimport json\nfrom pathlib import Path\nfrom urllib.parse import urlparse\nentries=json.loads(Path('/var/www/rss-offline/entries.json').read_text())\ncounts={}\nfor e in entries:\n    host=urlparse(e['link']).netloc.lower()\n    counts[host]=counts.get(host,0)+1\nfor host,count in sorted(counts.items(), key=lambda kv:(-kv[1],kv[0])):\n    print(f'{count:3} {host}')\nPY","timeout":30}},{"type":"toolCall","id":"call_Bb6t3n47rqUUzCwMkfVUYmsH|fc_0f029265420fe376016a1bfff0d6648191ac10521175b7bf5a","name":"bash","arguments":{"command":"python - <<'PY'\nimport json\nfrom pathlib import Path\nfrom urllib.parse import urlparse\nentries=json.loads(Path('/var/www/rss-offline/entries.json').read_text())\nby={}\nfor e in entries:\n    host=urlparse(e['link']).netloc.lower()\n    by.setdefault(host,[]).append(e)\nfor host in sorted(by):\n    print('\\nHOST',host)\n    for e in by[host][:2]:\n        print(e['local_path'].split('article-')[-1].split('.html')[0], e['link'])\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":259,"output":301,"cacheRead":98816,"cacheWrite":0,"totalTokens":99376,"cost":{"input":0.0006475000000000001,"output":0.004515,"cacheRead":0.024704,"cacheWrite":0,"total":0.0298665}},"stopReason":"toolUse","timestamp":1780219880619,"responseId":"resp_0f029265420fe376016a1bffe8b58481918dc950775043c8af"}}
{"type":"message","id":"7bb6d4c3","parentId":"6ad41fff","timestamp":"2026-05-31T09:31:28.984Z","message":{"role":"toolResult","toolCallId":"call_yiR2IjYhqTCSBho7pMD1i1lB|fc_0f029265420fe376016a1bfff0d6508191b3967d2ab9b44e3a","toolName":"bash","content":[{"type":"text","text":"  8 tailscale.com\n  7 github.blog\n  5 www.digitalocean.com\n  4 leadershipintech.com\n  3 gomakethings.com\n  3 simonw.substack.com\n  2 lucumr.pocoo.org\n  2 supernuclear.substack.com\n  1 mariozechner.at\n  1 martinalderson.com\n  1 openclaw.ai\n  1 world.hey.com\n"}],"isError":false,"timestamp":1780219888984}}
{"type":"message","id":"88cbe3fe","parentId":"7bb6d4c3","timestamp":"2026-05-31T09:31:28.985Z","message":{"role":"toolResult","toolCallId":"call_Bb6t3n47rqUUzCwMkfVUYmsH|fc_0f029265420fe376016a1bfff0d6648191ac10521175b7bf5a","toolName":"bash","content":[{"type":"text","text":"\nHOST github.blog\nc05e51dd412952e4 https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/\n98558fbdd89c6b67 https://github.blog/developer-skills/github/github-for-beginners-getting-started-with-git-and-github-in-vs-code/\n\nHOST gomakethings.com\nc9dde27b9de5a145 https://gomakethings.com/articles/how-to-redirect-if-a-page-doesnt-exist-with-apache/\n2a3be572f878e7ea https://gomakethings.com/articles/how-to-add-icons-to-external-links-with-css/\n\nHOST leadershipintech.com\nd44db85543e860b6 https://leadershipintech.com/newsletters/2287-why-japanese-companies-do-so-many-different-things\n2231f609093b63d6 https://leadershipintech.com/newsletters/2286-barely-treading-water\n\nHOST lucumr.pocoo.org\n897d93c50ad53a6e https://lucumr.pocoo.org/2026/5/26/clankers/\nb88b0610b56ab9fb https://lucumr.pocoo.org/2026/5/24/pi-oss/\n\nHOST mariozechner.at\n8214304532ff6317 https://mariozechner.at/posts/2026-05-30-shitty-robot\n\nHOST martinalderson.com\nc1ac4d327109cf09 https://martinalderson.com/posts/whats-going-on-with-gemini/?utm_source=rss&utm_medium=rss&utm_campaign=feed\n\nHOST openclaw.ai\n8509d34b794423ad https://openclaw.ai/blog/lighter-core-sharper-claws/\n\nHOST simonw.substack.com\nf419e05b4804e437 https://simonw.substack.com/p/i-think-anthropic-and-openai-have\nbc93d7b33addd1e2 https://simonw.substack.com/p/datasette-agent-an-ai-assistant-for\n\nHOST supernuclear.substack.com\n5b20f77bc9b2b5f2 https://supernuclear.substack.com/p/the-half-assed-potluck-and-other\n10bb2de89cd06bed https://supernuclear.substack.com/p/case-study-tamera\n\nHOST tailscale.com\n04009026a7f5cca2 https://tailscale.com/changelog/#2026-05-29-service\nf0c570917d1b9416 https://tailscale.com/changelog/#2026-05-28-client\n\nHOST world.hey.com\n8ce8fe6b9afb1b1c https://world.hey.com/dhh/basecamp-five-8fcfd2ef\n\nHOST www.digitalocean.com\nb5ddc6629f6a7c2d https://www.digitalocean.com/blog/ai-disruptors\nc22ede05fdcde865 https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\n"}],"isError":false,"timestamp":1780219888984}}
{"type":"message","id":"fe709209","parentId":"88cbe3fe","timestamp":"2026-05-31T09:31:37.913Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Inspecting selectors for hosts**\n\nI need to examine the selectors for each host, trying to make them generic enough for broader application. I could use a script to analyze potential candidate selectors and check their effectiveness. Let's see how I can structure it to ensure it covers what I need without being overly specific. It’s essential to get this right, as the selectors will play a significant role in functionality. I’m curious to see what I uncover!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bfff231a48191b2a586ad878967b5\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqG__0nesPrScN2srZeFfzHY5pUfu6tikPc7JBh3qNgcW6Hl2UN7jgZDBHpNm4asD6N9_dRyDB7mUU6G5HOOWtZqtnEO1_7xTp0nlrzgLPeGD2ajc3UcZzmoweXOg8oDHCe9BtBFj4MVRHBlsI4--x2bWoouAvyqK0lna0aB4OzOpV-w731Gf4jSbkwsw7PGLkR1tcwW6vGqhwsaNxFtsrgjaTb02XFpgqIvPXEyc3R8jJdaPsiEnXVHV1tANc3FW2ut0rQz5zNAe48VkTVWHSgyUVzSJ2I0dEASDikVeC6MgS7y8ezv2iCrqWrAxC2UUoZTNbSYGcQLLBRJpeGzinEKVjspMFwdkauGZVVlp-9Yy4sLRQ928C6-qoOHPhttyTvFDL3RJ1qxiT_n_Cn5mRgsdcgUE3KFAB-i_bHSndJSjouVucon2XMxnMQla00HQ6HbMiQgXx-jQkBiIopK0m2EGgTm7Rx9fQzMOaqWMOojjLbikQxjfMUGWmyAbwJlx78SEkFOlisXFyazM2_jE_quBZK0CLfdIrjEyDEHka2K11nadd7DE3Ghk3junR_4OH7qmjwTX9u4rPWoJ5nTmXjIr8EWPIZIr0UgmBXlbjltUhVAFqA7wgw8hI9skhUlUFKxwrQDp_M8DMvY33Br1wRL79LCnn8ibBfhbuZrGx_x_sv5PY6C5CXbgC9beOx9dkA9Q8eQWVYCFNEBA21YeSPN98PP7VLcH--8jGXCJPZOT6YdGbGTGg-S9kOb6WZuvgphq0Y10To6vkQA3pp6iwApE_mF3FjdYMjvZbhs93FZWiNqlLIEBGn4Az5HNxaS5_wMlhHziEMqySpQplqsh5cgJ9TfqZpsxKYpMsdzifsQn6ceqCEwvy1UUkP3RhokE86MCr7_qnt8wDIi1dylaU-QGbvYaPFQ2GIaPi6NQQcs_TdgoO9G_H6ByjfRfEfH3CKA-q7s1DPO6C8IbppwhYqX6G6XPOe3JxXioMZoDQI9MdlMHLxp6PP1xrol-hwTJy1tk5zkvrWSCddfsb8yTDApSldUGamaFiaVm-fmriaVXoabM=\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Inspecting selectors for hosts**\\n\\nI need to examine the selectors for each host, trying to make them generic enough for broader application. I could use a script to analyze potential candidate selectors and check their effectiveness. Let's see how I can structure it to ensure it covers what I need without being overly specific. It’s essential to get this right, as the selectors will play a significant role in functionality. I’m curious to see what I uncover!\"}]}"},{"type":"toolCall","id":"call_ETM4SErQc5nfmbo6NKIoc7LH|fc_0f029265420fe376016a1bfff4b6a081918f3e5889b3816675","name":"bash","arguments":{"command":"python - <<'PY'\nfrom bs4 import BeautifulSoup\nfrom pathlib import Path\nsamples={\n'gomakethings.com':'c9dde27b9de5a145',\n'leadershipintech.com':'d44db85543e860b6',\n'lucumr.pocoo.org':'897d93c50ad53a6e',\n'mariozechner.at':'8214304532ff6317',\n'martinalderson.com':'c1ac4d327109cf09',\n'openclaw.ai':'8509d34b794423ad',\n'simonw.substack.com':'f419e05b4804e437',\n'supernuclear.substack.com':'5b20f77bc9b2b5f2',\n'tailscale.com':'04009026a7f5cca2',\n'world.hey.com':'8ce8fe6b9afb1b1c',\n}\nfor host,name in samples.items():\n    soup=BeautifulSoup(Path(f'/var/www/rss-offline/article-{name}.html').read_text(),'html.parser')\n    print('\\nHOST',host)\n    cands=[]\n    for tag in soup.find_all(True):\n        txt=len(tag.get_text(' ',strip=True))\n        if txt < 250:\n            continue\n        ident=' '.join(tag.get('class',[])) or tag.get('id') or ''\n        if ident:\n            cands.append((txt,tag.name,ident[:160]))\n    for txt,tag,ident in sorted(cands, reverse=True)[:18]:\n        print(txt, tag, ident)\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1366,"output":420,"cacheRead":98816,"cacheWrite":0,"totalTokens":100602,"cost":{"input":0.003415,"output":0.0063,"cacheRead":0.024704,"cacheWrite":0,"total":0.034419}},"stopReason":"toolUse","timestamp":1780219888986,"responseId":"resp_0f029265420fe376016a1bfff10f54819182d9ec80be2b4af2"}}
{"type":"message","id":"a3ef2016","parentId":"fe709209","timestamp":"2026-05-31T09:31:38.835Z","message":{"role":"toolResult","toolCallId":"call_ETM4SErQc5nfmbo6NKIoc7LH|fc_0f029265420fe376016a1bfff4b6a081918f3e5889b3816675","toolName":"bash","content":[{"type":"text","text":"\nHOST gomakethings.com\n2018 main margin-end-7xl\n1942 div container-m\n421 pre chroma\n421 div highlight\n421 code language-apacheconf\n\nHOST leadershipintech.com\n3835 body leadershipintech newsletters show\n3342 div campaign\n\nHOST lucumr.pocoo.org\n11775 div container\n11359 div body\n334 div footer\n\nHOST mariozechner.at\n22312 div mx-auto p-6\n254 div toc\n\nHOST martinalderson.com\n7379 div post-layout\n7017 article post\n6851 div post-content\n773 section footnotes\n773 ol footnotes-list\n494 li footnote-item\n346 aside post-sidebar\n278 li footnote-item\n\nHOST openclaw.ai\n3461 main reader-shell\n3360 article article\n2843 div article-content\n671 section timeline-list\n392 section mini-card-grid\n363 section chart-grid\n274 footer article-footer\n\nHOST simonw.substack.com\n42226 div main typography use-theme-bg\n42226 div entry\n42049 div single-post-container\n41046 div single-post\n41046 div pencraft pc-display-contents pc-reset pubTheme-yiXxQA\n41046 div container\n41046 article typography newsletter-post post\n40223 div body markup\n40223 div available-content\n1002 div pencraft pc-display-contents pc-reset pubTheme-yiXxQA\n523 div single-post-section\n523 div portable-archive\n523 div pencraft pc-paddingTop-24 pc-paddingBottom-24 pc-reset\n523 div container\n500 div portable-archive-list\n492 div container-O1YsI6 two-column-list-BLHtzo two-column-list--with-dividers-cHfR0M\n452 div pencraft pc-display-flex pc-flexDirection-column pc-gap-16 pc-paddingTop-32 pc-paddingBottom-32 pc-reset\n407 div single-post-section comments-section\n\nHOST supernuclear.substack.com\n8525 div main typography use-theme-bg\n8525 div entry\n8361 div single-post-container\n7179 div single-post\n7179 div pencraft pc-display-contents pc-reset pubTheme-yiXxQA\n7179 div container\n7179 article typography newsletter-post post\n6737 div body markup\n6737 div available-content\n1181 div pencraft pc-display-contents pc-reset pubTheme-yiXxQA\n750 div pencraft pc-display-flex pc-flexDirection-column pc-gap-16 pc-paddingTop-32 pc-paddingBottom-32 pc-reset\n705 div single-post-section comments-section\n705 div container\n686 div comment-list-items\n686 div comment-list post-page-root-comment-list\n461 div pencraft pc-display-flex pc-gap-12 pc-paddingBottom-12 pc-reset comment-content\n461 div pencraft pc-display-flex pc-flexDirection-column pc-reset flex-grow-rzmknG\n461 div comment\n\nHOST tailscale.com\n228549 html __variable_982c6a\n228527 body __variable_da76f7\n225991 main container\n225918 section mt-10 overflow-hidden\n225885 div container space-y-10 md:space-y-20\n4491 article flex w-full max-w-[1060px] scroll-mt-40 flex-col gap-y-3 px-2 md:flex-row md:gap-[30px] md:px-0\n4479 div md:flex-[1_1_843px]\n4479 div changelog-date-group Markdown Markdown--changelog dark space-y-6\n3727 div changelog-entry scroll-mt-28\n3689 div t-b18 changelog-entry\n3678 article flex w-full max-w-[1060px] scroll-mt-40 flex-col gap-y-3 px-2 md:flex-row md:gap-[30px] md:px-0\n3665 div md:flex-[1_1_843px]\n3665 div changelog-date-group Markdown Markdown--changelog dark space-y-6\n3620 article flex w-full max-w-[1060px] scroll-mt-40 flex-col gap-y-3 px-2 md:flex-row md:gap-[30px] md:px-0\n3612 article flex w-full max-w-[1060px] scroll-mt-40 flex-col gap-y-3 px-2 md:flex-row md:gap-[30px] md:px-0\n3607 div md:flex-[1_1_843px]\n3607 div changelog-date-group Markdown Markdown--changelog dark space-y-6\n3599 div md:flex-[1_1_843px]\n\nHOST world.hey.com\n3529 main page page--medium@medium\n3529 div page__content\n3086 section push_double--ends pad--bottom\n3086 div trix-content\n363 div push_double--top align--center\n258 div trix-content\n258 div bio txt--small txt--subtle push--bottom\n"}],"isError":false,"timestamp":1780219898835}}
{"type":"message","id":"68a2caf3","parentId":"a3ef2016","timestamp":"2026-05-31T09:31:50.747Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Crafting host selectors**\n\nI need to create selectors for different hosts, considering things like a substack article or typography for a newsletter post which might include comments or archives. I’m thinking of using body markup or available content. For Tailscale, I could isolate sections if the target body is large. Maybe I should add a host selector for the changelog entry as well. It's tricky, but I’ll look for generic class selectors and target specific tags like \".campaign\" for other implementations. I want to ensure everything is robust!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1bfffc1cbc819182467d8ea0337556\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAADcyQ_ExeRFUnf5nFpv05UL7BKLg5EjHPGCIAuncVIr5-0TM3-5BYCag7CaZEjq-r27VESyQkuL8kN9zwKhlJDjTV9pIKQ-xM9M-Xzq4nVWFeY-bt_xmbLLYo54M_IvXhyr6lw6Q0c1_3zEcL8iWZP8CUogSKMkKVb4ZjRbL8guN46ERVcTivA2u6pfb5wMvJ-v53J0-1pZ2aIBPsRYjZWNV_GUZcscIb7CCOThOaxb0DPw7vUgACpOW-NGVng8iGHeKPL_kc87qRYy57QGyjCou7CSLF6CWstnx7VxoF8ar8LyWIfP2gdnnka5TA3QLLQWAxiRdJqgOv1Y4p1sh-lw_r2gwQ0nSjfaml1vtDyKG3_TF7xNMWk9VyB9rz2oG-paDg9FGwXsj50LselNNeuSDUd2XMD2lK9n-79ipRy_sVfYUkxO_-6m-1Olec4ox6l08_VZgm1Uo2v_SWOOLr18t6HAiiir_LLs7IEVpeoH0aL7HuAoa6uvOJQ5hyJsayHH0v7gHDGHNxTzviKnovuh_7L8u1AXzFhAejYr8jnNCDtIczC6_qp65hgNdR9U4ahz_-jb_y-JBrcKycVlH0b_aQjzLbE0EVhli3sLOlKpe9FmLZiZ1fEX4b91E8mlbJ1m1rs08P2ss0WyjG-Ondq51CcxGFqikxMSrYgwxQ5KLjREVo2RV-nAU0yTKx4zick33RC06qaC8QxFkWXLWTk_bZ7NhQ1cbUvVn97qw-oG6ERVfWE4KYFClJ3kxTjusP9wuiMDoar9rjOa-4jDBK8DoTqUHeppW3bkbXl05ZDJ6st6oQdzJ67dE9rtM64K2gr1uC-nkzQ170a0cWgFqCEOAQ4Tv2av9p7VpPoVID5gK16LoIontRml2lUfl5fRAWWzCkGXRcnai3v8i57ZUxZfZNexhuggbk0AHOBUuGRCBr8euCrdHSTvUbxgm81vmtCRVo-LyTTIS1PYDDbBmDcTZqpdDqmzKcDI-NRJMTTJHREPkyAhpHmcpth519WBrO3kCNr0ytaG5C63G3hYlRx0mW4epwr8EbKh0dA5VbJ9PPJLM8mr9dH9JLuOD1Pwu1K8lAOGdqPk2co0LmUS20MqCALHzzlLycyEEPRO0aPj-5Jb8f5YcNSWzRZGzXmfbR4EDs1cxtrOWUC6sNlOrKBjXYJeSqNto6DIYeAcnN24sbFqa6KfatEAIDcoZ1Kkls06UEr6PZ5KBK0-i5VSoqjyjpkHrbCIiJ-IPG11F4pFhBXoeKg0RRy1v-6CkHs3rQn_14GOaTJzowrc9b0bQza3dLyKQiUMJyq4rvT1mzarJ4mM8JIjBTuRRd6Ce2LOIoOfwLh5L8R1_5jDE1P953SRw49kRtzK7Rq2fyArVn3FDdCfAKE9QDYCWDYZPNPmvTeBerv9uW4EzFX49c9-XHqxrq6wbwMUK5l_53u_D8OQDl2JC13Mp54O3jqHA51c-Gq64GC4fmshN182pAp-bYgsW7tC2PYUZYJloZdQCITbZK9IXSIVnNOXgzzRN641ipObkVN9v5lxQOhN2N0ZCLcFVY1AC0PRjnZBDqXwbCnWxeLWq6044Pmn5OI4QSAU2rwRX8YzTc82cHI3E9if3CsMsttNmjdp6gJCDdqfoV0N4ggmZiLnPL0rIMRBcOKSciTN3SCzQWJbZhB_qrlW8aFyhWXCoDYMLbGxjjPFESOUUXPMPVEBHsnLhJCYzIxFAU3y2FC_cw2bo12vl-4IhwCWJILh-C6tFtqItsqpDujRzNxYUns1ctEnYApfwTH8_Zvgdmpz_gc5Pdq5FTNfAQg3qV0BxeVZPd6krajpKp39aIWwF4k3npZxv--DtVzpqdTLJCDZu4ua0ALr2Xi0Ro7bXk9sJ7cZReyP5S6m45m1D2p9MS7hkHcYz_LU7fpCl2W3wZS6E9BgZttGbjp8FuZBtZXN0Q7ihLQUYzW63yBySKoSYEQKoKXTL2XC65PmNVw-ySdCMjnxCThfF1O7YlC_gv-jQU8N3XASTMw7VYC6npFv3kyhK7oTNQ93zpRSiMtKw3w2WKHbYWkL_T_48R_G6hI4nOYNfZy0ZlcFofYiLOyaS17RJY3KuqVnERGD9T-7vwD4bkz328AZl8hsC3H-4u-PIi-W9z2gqMLEXZg9FL0Rfhy93nADZJ9gAeufv77\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Crafting host selectors**\\n\\nI need to create selectors for different hosts, considering things like a substack article or typography for a newsletter post which might include comments or archives. I’m thinking of using body markup or available content. For Tailscale, I could isolate sections if the target body is large. Maybe I should add a host selector for the changelog entry as well. It's tricky, but I’ll look for generic class selectors and target specific tags like \\\".campaign\\\" for other implementations. I want to ensure everything is robust!\"}]}"},{"type":"toolCall","id":"call_qdGhmZ590OdDXL3RAM3x1lxU|fc_0f029265420fe376016a1c0003389481919642bc5bda1cbe53","name":"bash","arguments":{"command":"python - <<'PY'\nfrom bs4 import BeautifulSoup\nfrom pathlib import Path\nchecks={\n'leadershipintech.com':'d44db85543e860b6',\n'lucumr.pocoo.org':'897d93c50ad53a6e',\n'mariozechner.at':'8214304532ff6317',\n'martinalderson.com':'c1ac4d327109cf09',\n'openclaw.ai':'8509d34b794423ad',\n'simonw.substack.com':'f419e05b4804e437',\n'supernuclear.substack.com':'5b20f77bc9b2b5f2',\n'world.hey.com':'8ce8fe6b9afb1b1c',\n'gomakethings.com':'c9dde27b9de5a145',\n}\nselectors=['article','main','[role=main]','.post-content','.article-content','.available-content','.body.markup','.campaign','.trix-content','.post-layout','.post','.entry-content','.content','.container-m']\nfor host,name in checks.items():\n    soup=BeautifulSoup(Path(f'/var/www/rss-offline/article-{name}.html').read_text(),'html.parser')\n    print('\\nHOST',host)\n    for sel in selectors:\n        nodes=soup.select(sel)\n        if nodes:\n            best=max(nodes,key=lambda n: len(n.get_text(' ',strip=True)))\n            print(sel,len(nodes),len(best.get_text(' ',strip=True)))\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1794,"output":553,"cacheRead":99840,"cacheWrite":0,"totalTokens":102187,"cost":{"input":0.004485,"output":0.008295,"cacheRead":0.02496,"cacheWrite":0,"total":0.037739999999999996}},"stopReason":"toolUse","timestamp":1780219898837,"responseId":"resp_0f029265420fe376016a1bfffae7788191981e384ac33a28d8"}}
{"type":"message","id":"bd8d7464","parentId":"68a2caf3","timestamp":"2026-05-31T09:31:51.420Z","message":{"role":"toolResult","toolCallId":"call_qdGhmZ590OdDXL3RAM3x1lxU|fc_0f029265420fe376016a1c0003389481919642bc5bda1cbe53","toolName":"bash","content":[{"type":"text","text":"\nHOST leadershipintech.com\n.campaign 1 3342\n\nHOST lucumr.pocoo.org\n\nHOST mariozechner.at\narticle 1 22266\n\nHOST martinalderson.com\narticle 1 7017\nmain 1 7379\n.post-content 1 6851\n.post-layout 1 7379\n.post 1 7017\n\nHOST openclaw.ai\narticle 1 3360\nmain 1 3461\n.article-content 1 2843\n\nHOST simonw.substack.com\narticle 1 41046\n[role=main] 1 42049\n.available-content 1 40223\n.body.markup 1 40223\n.post 1 41046\n\nHOST supernuclear.substack.com\narticle 1 7179\n[role=main] 1 8361\n.available-content 1 6737\n.body.markup 1 6737\n.post 1 7179\n\nHOST world.hey.com\narticle 1 3086\nmain 1 3529\n.trix-content 2 3086\n\nHOST gomakethings.com\narticle 1 1684\nmain 1 2018\n.container-m 1 1942\n"}],"isError":false,"timestamp":1780219911420}}
{"type":"message","id":"cdd65881","parentId":"bd8d7464","timestamp":"2026-05-31T09:31:55.649Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1c0008a07481919841c7dc9242ec61\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAAJq63CV9oy2Yj7ImIa1g7Cy3G7ZXbeqBLB9Bw7qDSxgq4Ane_wL0n58BX90-Am0PHOvel-A746c4RTwjsiB783hujlA_tSJN7aa8FKwF9vhdi_Iw5xa5WsyO8jBJ-U5KgLr6BRxms6BegOEEjCJ8e4_KWWAo_lMu5L4B42yI9kaWaGCpiPx91QsYU2yQmj0kzaV8MqdO8tayGor0ASobVDJfIPZL-sjcqHTXIFuVmtEWYDH6h29e1Qek60TSUouFYp74C2ZZxTI-Tby8lJVrSq1ughaTADW39whOTMEhWZEJam06R_Fef4Jo2rc6Atd1gMRrByNmAX42Mq7TL9fOO-RLRu2MEHqlPReSRGept39cpIgfSkxBbdX233Do-YfA4ojoXXfrq1RMwyGbGG_0ErJORlaPp9Fxn08w8q2ZKghyzPPmQee-RzD895dfxvjqKGKLmu7gkDIcfCyCtPwRTqUjvZnkZqI9-7qQhTDdTQNiVYaxQZcCG6amWTIOPcu0h5ryX-b_w2n8gHglwEEwEhhbEMwds2SddGEYmArS1YMaiRP8yEfSrTVc4Fzr6PsEByArmEgny3eKvfD0KdENBeg-NkrTGyqLYq2xTTXKLNGypxGR1MAPtxx2iURwc73JyxR4nQkTjVkWpuqLTxUfjoKN3PiJSmN3ijwL8lApX53adNJj8TFNcyy31mHoezCCBC8SuoS98W54cJy0_3iAVHADfbYqwyOAHwhRY7OJQqnaW84bDG1S5_yZNEnTDk0xchckr48pj4UxPGSZ4KOBpOxVoFkDDpk0Ru91IAJt9uQGLXt2HkC7w2TG1TVRUGPiYhbC0PCmlNBWr4RLxvlU_4RYuoxvDQUmDwvg2eYEW-GsjM1XOsJuQILz6o3J5NXDjUrJ3fO9FYQCEh7q6Jhfk72TqVwlKUP4MvOVZQpV4UidTTgmsBgZ3lQDaf_A5SPnuvZNftmk6iB9hfcBflVGtdzg==\",\"summary\":[]}"},{"type":"toolCall","id":"call_LBtHV15C2Kt8Y9EiOg2ePyz2|fc_0f029265420fe376016a1c00096a108191ae50c47fa86ec150","name":"bash","arguments":{"command":"python - <<'PY'\nfrom bs4 import BeautifulSoup\nfrom pathlib import Path\nsoup=BeautifulSoup(Path('/var/www/rss-offline/article-897d93c50ad53a6e.html').read_text(),'html.parser')\nfor tag in soup.find_all(True):\n    txt=len(tag.get_text(' ',strip=True))\n    if txt<500: continue\n    ident=' '.join(tag.get('class',[])) or tag.get('id') or ''\n    if ident:\n        print(txt, tag.name, ident[:200])\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1089,"output":160,"cacheRead":101376,"cacheWrite":0,"totalTokens":102625,"cost":{"input":0.0027225,"output":0.0024000000000000002,"cacheRead":0.025344,"cacheWrite":0,"total":0.0304665}},"stopReason":"toolUse","timestamp":1780219911422,"responseId":"resp_0f029265420fe376016a1c00077bf88191b42215f9daefc429"}}
{"type":"message","id":"2a7ae2ea","parentId":"cdd65881","timestamp":"2026-05-31T09:31:55.881Z","message":{"role":"toolResult","toolCallId":"call_LBtHV15C2Kt8Y9EiOg2ePyz2|fc_0f029265420fe376016a1c00096a108191ae50c47fa86ec150","toolName":"bash","content":[{"type":"text","text":"11775 div container\n11359 div body\n"}],"isError":false,"timestamp":1780219915881}}
{"type":"message","id":"0420d31e","parentId":"2a7ae2ea","timestamp":"2026-05-31T09:32:18.283Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Revising URL Handling**\n\nI'm thinking about how to manage fragment-based URLs. I wonder if I should reorder the process to handle isolation before selecting the root. The current order isolates after picking the root, which could be problematic if the root doesn't include the target. For fragment-specific pages, the root must still contain the target. It might make sense to adjust the function to skip picking the root if a fragment exists unless the selector includes the target. I'll update the order accordingly and check if it still works for GitHub and DigitalOcean.","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1c000dbdd081919df35b7fa1988fa2\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAAVZ9XmfaMnaVvU34fjJnjyiQ2hGf0P1L-dyF3kaEfs4a_KnvRkYm_UDVA4x0ieLBUjSHodjXtOw51RaMq_cvo66NUu9xAuQcAn0XhMFnwWi1AyL9UA2h49-1n2fk6nww1Fuxq43vfGzc_jC5h-iYWHZQeue6jMfB63UjNuXFt6Pmau7yagpwyBeiV4aA5UeF3oHPROq5TMJ6tMa0CX5GWzlnrQMUgFaa49riNFOE_60uFJumY6KjhsPZWui3597kGrdGwrRHdsgnHLNr8K6nXFiQCEBaUfuST9Iv8HUyrZNDaGwNZqAU8DgWl8MRCIvYn6-ex1RVlOXJ88uB5PAsubpCr73y5hXQi5n7jFKW92iz_U4c59-e0Lsq3opdX5yyACZNUH3TRMTk13Hmo-VGik5b5sFNY_auR4XRVPShxe5oEvtjsE_rlUiNUVLkWh27_x1s94yvddPPSpJfjxnQlStcB69fXhbhKXMY-OBXClggjcj7ZgcvQLU_OVXvDgpAHOzSKaotKeIAyHflhXOfXSyMG-T7FiuigyhJAZTClaCKdh_rpJeLv6eZ7udI4OpdDdcszNF52VT31ZF9yB7cDpww6McQX1IolkB5FWfwipW7VkYvcpckrXIE5AGK-XqZuGUlsvwjcZbcPO9rl6tX4uYyzke7-tRdEboIoVR6ZnujhYAPfs1KqW0EePvK_ZwODopnTgYCJe-DRiPwLd5fQ1NvZMf1nGCRVBMAC5td1RQG_LWHCCfQO4TNYr8T6v8psQCGLyRM7aAOwhvWBzk5nF7hL2cj5oT8RtU7HUqfrWnNUIkPMjL5WLmPzsIQeZ138G_dAJwtX4lMdl97_eApY7YO04UAO_uiYsOYeRlahS1TZhrRlIb6KA-eQv9ldfcCCfkjo7Sk6cgA1ZlEJklvkN6vcA0PHTl18EZ8Ip7UBCDBW0VmZd2tw0JXkVKRNE8J1uswX5OT2M7G--xlZrzujRv68BNJ8wjY2nrVjAJx68tQpOJVSQy5ZRqMloAJG2gniq9hIc1F4bEowBslumnGrKQ03QVtSFAKRO3qi01FSjUw_nC2ymsGUciDpYg74mnSxoRa6hU1QOp4D4ujWw_LiELwV2vfMIjdvYrV6AKEieRPc-zInpWvUA8qvbHwD7is2DuID5fjhtXkTpOajGZN9TyWfr-uWSbpFnmQ6dhG512TqvfmRr3TEG6fz96QTcY7voEgERXIAFUPE39gbOk10yd8qi8uTvPg_Tq6PdgeCGydxc4OxzS_HJz4Ch6lOpmdn0sBVqi080RQRBCkgsCTgfXU4FKJpEG03ut3q2Rc5y3WeWXsTz9Guz6UsUQ-KkrDzUJjs056O0Ti1BrVmHMvPuTDKlYhRz5hR0yVSG0ckaykMJka1-evqt2dpVQx3-sgKNSEypoGsDUIiWw4YQggY_8HLPzrqmirNSuwMmj1rajyQYU8NLO8wo2A-hg2LzCvD3tCiU_iLLW8sb3YejjU3gPmA95HPE2Fo7RBSNPjeRtzz4Yn7kjlRMp0EKNcrp-MyXC2C3KmRZDlkvjCW26OMTopOc9EwN6L3DSHDwox7AeIg44BkOuvW-ENnU8KpEkZUTP_HzUDKs10krzhtSkiLYxAZc_N068P097SGktKQa9515xUsaaE9PAHcoktpNnhuAQ06zgaPjglehkyEUQli1D90xk28oblesH9Fw7KT4yjchbllPR-Yi4BHYnPNZ3UEISsb2rByzp0eZOuFUATxd__ncG7X-Vh-VW1xobRZ0BHgVzDoYKdWc5cvpEBJa0QVwuF7PQU7jEQncrDM0g8qkbThAmz8MfLEyMlMdNsjSR8PrHQUTCFFfgRj_ZgIk9ZT9iscAtoSoMv8QpORSRmxou5-TQBVNBzO9Y-3wNSdulwZ6b2iDWT9nmSO4A3hl-xqqyrgfBIyG1bep97IviSy4p0lZZu-2GL15fGvcvTEQrInyvkYOCwNRLyCl9RI20kkYBp1i4bf9dPK7qnc4fkLQtRyutIzyIa6atxicH8fvZFp-TZNWl6WkrY2oIly7Xq2AtBXjuWv13tMQztoY0C_UXsXoULupuxifZUCcQvqkUVYpJhmEMs_vs584s2OAcjUUXT4NFRmsaKyQB83AAYdZ9zAPX9CdeblCLTROJJmVefHzN-ba1TjTXkGJNIdUe5QImtxZSvtJ6JGjEIhX2qh_ENc9rw-GtHnNiKNuYZdEWMFjG5jxAw8JK3NENk5H7p14q2fBPCjNBLlM07bcqnhuUpUHTgg-F7KjLFKCW7vj4DQIosFM6dz8mna7-XCIr6OEqpQT57QChY7La6vq062x_CBXfeAoczZyad7x8ZlgZ-BRYZOmMFfj5f5a8-_3pv-uaNafhwrydrpg3C6SjN6Svi8TK9g2vH4Yy5qadSV2Ng4coWQqyHn9zAlsvgtcaYH8VIAh3wQ5rf08hWP9M9PLdD5GRB3KDnSIpGXMdX07RF9FDF5C-cBiQjFocc9nHrpgF2QphH9QOwyRwLRnJXJjBAwuszzoyC0hd4o0YsKERMJ47XMi9bkksLOuh2gqhLoQVA_vqZ-vFLBv_CLF0bLArP-gNWb9uaEhIcf4A1QIkZJjD1t-6btqBsMchTYlTDDgvWqJ3IpVM3uZZH_VwpPqLB_xbGNaN4Jw7nmAFBg-yn7Yg65E3fEEfBm8fuMO7GJckMJ8BhZujglh38zkoamVQXHlu0ntReb1QbOhc9QFJdHkBdAuCpOqxPKg4StlHgXgVIutk1HBvR74ChduQ-D3zKfTnvuE4JIf4LNgQz3ClIDtprfNHZTI3FM1YuGWwIuYk43mAVtIJFuggi5MB1ZZnb3rWJfwVMAlF8q3_-EUa8nv_bohkK19m59wxlxY2eHDGNCQDT42dHLPspjh1-Blj0MLNjJmtlRQSDl0rI0RwgZiPkAkogufPoSePBnyGdczip76-h_eP46S-ne9XyhRTjhR_WMTELeJUbMb9GzUIUwMbCigJjZ3WBbe2lGneeI9uTUZPk1QSirVIVuTvxm-nokTF6kFX1rD2RnancZig9q-7uzl0g4SLnRscXhHNXAntOW7fW-ASHiePTF_k4BbVgTCJGByINtPXoA0NYW30x3AtbDcGADkcFWbknzK1kKnUxJyBV9oxXfTh57JUr506n-CV2fv6_-NIb14rFEOK4uuD5C7pf1wCmHK1BbmynMJITDwvz0VUFMzk8_gIMsCBVuJNixItnHLVQ3_usoNuJPhV7GmiinO67ha0szmPuL-7naNQrcS1Ozd1EDZv7hddRAc1zfR10ApQvjh3a_5Q02KEXV0rzTiLC_tdNLu9_F38L9WcJcjwHljoSjEnGPV9KGla3s3tvwVGhMwk7oN9FY=\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Revising URL Handling**\\n\\nI'm thinking about how to manage fragment-based URLs. I wonder if I should reorder the process to handle isolation before selecting the root. The current order isolates after picking the root, which could be problematic if the root doesn't include the target. For fragment-specific pages, the root must still contain the target. It might make sense to adjust the function to skip picking the root if a fragment exists unless the selector includes the target. I'll update the order accordingly and check if it still works for GitHub and DigitalOcean.\"}]}"},{"type":"toolCall","id":"call_F2aXiBD67Ih3UOJATj8wy3iQ|fc_0f029265420fe376016a1c0015eaac81918334064e64dd271c","name":"edit","arguments":{"path":"/home/sebas/work/projects/rss-offline/build.py","edits":[{"oldText":"def pick_print_root(soup: BeautifulSoup, entry: dict):\n    host = urlparse(entry['link']).netloc.lower()\n    selectors = []\n    if host.endswith('github.blog'):\n        selectors = [\n            'section.post__content',\n            '[class*=\"post__content\"]',\n            'main .post__content',\n        ]\n    elif host.endswith('digitalocean.com'):\n        selectors = [\n            '[class*=\"MarkdownStyles__StyledMarkdown\"]',\n            '[class*=\"blog-slug__StyledPost\"]',\n            '[class*=\"Sidebar__StyledSidebarContent\"]',\n        ]\n\n    for selector in selectors:\n","newText":"def pick_print_root(soup: BeautifulSoup, entry: dict):\n    host = urlparse(entry['link']).netloc.lower()\n    selectors = []\n    if host.endswith('github.blog'):\n        selectors = [\n            'section.post__content',\n            '[class*=\"post__content\"]',\n            'main .post__content',\n        ]\n    elif host.endswith('digitalocean.com'):\n        selectors = [\n            '[class*=\"MarkdownStyles__StyledMarkdown\"]',\n            '[class*=\"blog-slug__StyledPost\"]',\n            '[class*=\"Sidebar__StyledSidebarContent\"]',\n        ]\n    elif host.endswith('substack.com'):\n        selectors = [\n            '.available-content',\n            '.body.markup',\n            'article.newsletter-post',\n        ]\n    elif host.endswith('tailscale.com'):\n        selectors = [\n            '.changelog-entry',\n            '.Markdown--changelog',\n        ]\n    elif host.endswith('gomakethings.com'):\n        selectors = [\n            'article',\n            'main',\n        ]\n    elif host.endswith('leadershipintech.com'):\n        selectors = [\n            '.campaign',\n        ]\n    elif host.endswith('martinalderson.com'):\n        selectors = [\n            '.post-content',\n            'article.post',\n        ]\n    elif host.endswith('openclaw.ai'):\n        selectors = [\n            '.article-content',\n            'article.article',\n        ]\n    elif host.endswith('world.hey.com'):\n        selectors = [\n            '.trix-content',\n            'article',\n        ]\n    elif host.endswith('mariozechner.at'):\n        selectors = [\n            'article',\n        ]\n\n    for selector in selectors:\n"},{"oldText":"    soup = BeautifulSoup(src_path.read_text(), 'html.parser')\n    soup = pick_print_root(soup, entry)\n    soup = isolate_anchor_section(soup, entry)\n    soup = clean_pdf_soup(soup)\n","newText":"    soup = BeautifulSoup(src_path.read_text(), 'html.parser')\n    soup = isolate_anchor_section(soup, entry)\n    soup = pick_print_root(soup, entry)\n    soup = clean_pdf_soup(soup)\n"}]}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":758,"output":1132,"cacheRead":101888,"cacheWrite":0,"totalTokens":103778,"cost":{"input":0.0018950000000000002,"output":0.016980000000000002,"cacheRead":0.025471999999999998,"cacheWrite":0,"total":0.044347}},"stopReason":"toolUse","timestamp":1780219915884,"responseId":"resp_0f029265420fe376016a1c000bf3a88191a5ffe9c567b030d5"}}
{"type":"message","id":"17762dc8","parentId":"0420d31e","timestamp":"2026-05-31T09:32:18.333Z","message":{"role":"toolResult","toolCallId":"call_F2aXiBD67Ih3UOJATj8wy3iQ|fc_0f029265420fe376016a1c0015eaac81918334064e64dd271c","toolName":"edit","content":[{"type":"text","text":"Successfully replaced 2 block(s) in /home/sebas/work/projects/rss-offline/build.py."}],"details":{"diff":"     ...\n 313             '[class*=\"MarkdownStyles__StyledMarkdown\"]',\n 314             '[class*=\"blog-slug__StyledPost\"]',\n 315             '[class*=\"Sidebar__StyledSidebarContent\"]',\n 316         ]\n+317     elif host.endswith('substack.com'):\n+318         selectors = [\n+319             '.available-content',\n+320             '.body.markup',\n+321             'article.newsletter-post',\n+322         ]\n+323     elif host.endswith('tailscale.com'):\n+324         selectors = [\n+325             '.changelog-entry',\n+326             '.Markdown--changelog',\n+327         ]\n+328     elif host.endswith('gomakethings.com'):\n+329         selectors = [\n+330             'article',\n+331             'main',\n+332         ]\n+333     elif host.endswith('leadershipintech.com'):\n+334         selectors = [\n+335             '.campaign',\n+336         ]\n+337     elif host.endswith('martinalderson.com'):\n+338         selectors = [\n+339             '.post-content',\n+340             'article.post',\n+341         ]\n+342     elif host.endswith('openclaw.ai'):\n+343         selectors = [\n+344             '.article-content',\n+345             'article.article',\n+346         ]\n+347     elif host.endswith('world.hey.com'):\n+348         selectors = [\n+349             '.trix-content',\n+350             'article',\n+351         ]\n+352     elif host.endswith('mariozechner.at'):\n+353         selectors = [\n+354             'article',\n+355         ]\n 317 \n 318     for selector in selectors:\n 319         nodes = soup.select(selector)\n 320         if not nodes:\n     ...\n 354     pdf_dir = OUTPUT_DIR / PDF_DIRNAME\n 355     pdf_dir.mkdir(parents=True, exist_ok=True)\n 356     printable_path = pdf_dir / f'article-{slug(entry[\"link\"])}.print.html'\n 357     soup = BeautifulSoup(src_path.read_text(), 'html.parser')\n-358     soup = pick_print_root(soup, entry)\n 359     soup = isolate_anchor_section(soup, entry)\n+398     soup = pick_print_root(soup, entry)\n 360     soup = clean_pdf_soup(soup)\n 361     html = str(soup)\n 362     html = html.replace('/rss-offline/', 'file:///var/www/rss-offline/')\n 363     html = html.replace('position:fixed;top:0;left:0;right:0;', 'position:static;')\n     ...","patch":"--- /home/sebas/work/projects/rss-offline/build.py\n+++ /home/sebas/work/projects/rss-offline/build.py\n@@ -313,8 +313,47 @@\n             '[class*=\"MarkdownStyles__StyledMarkdown\"]',\n             '[class*=\"blog-slug__StyledPost\"]',\n             '[class*=\"Sidebar__StyledSidebarContent\"]',\n         ]\n+    elif host.endswith('substack.com'):\n+        selectors = [\n+            '.available-content',\n+            '.body.markup',\n+            'article.newsletter-post',\n+        ]\n+    elif host.endswith('tailscale.com'):\n+        selectors = [\n+            '.changelog-entry',\n+            '.Markdown--changelog',\n+        ]\n+    elif host.endswith('gomakethings.com'):\n+        selectors = [\n+            'article',\n+            'main',\n+        ]\n+    elif host.endswith('leadershipintech.com'):\n+        selectors = [\n+            '.campaign',\n+        ]\n+    elif host.endswith('martinalderson.com'):\n+        selectors = [\n+            '.post-content',\n+            'article.post',\n+        ]\n+    elif host.endswith('openclaw.ai'):\n+        selectors = [\n+            '.article-content',\n+            'article.article',\n+        ]\n+    elif host.endswith('world.hey.com'):\n+        selectors = [\n+            '.trix-content',\n+            'article',\n+        ]\n+    elif host.endswith('mariozechner.at'):\n+        selectors = [\n+            'article',\n+        ]\n \n     for selector in selectors:\n         nodes = soup.select(selector)\n         if not nodes:\n@@ -354,10 +393,10 @@\n     pdf_dir = OUTPUT_DIR / PDF_DIRNAME\n     pdf_dir.mkdir(parents=True, exist_ok=True)\n     printable_path = pdf_dir / f'article-{slug(entry[\"link\"])}.print.html'\n     soup = BeautifulSoup(src_path.read_text(), 'html.parser')\n-    soup = pick_print_root(soup, entry)\n     soup = isolate_anchor_section(soup, entry)\n+    soup = pick_print_root(soup, entry)\n     soup = clean_pdf_soup(soup)\n     html = str(soup)\n     html = html.replace('/rss-offline/', 'file:///var/www/rss-offline/')\n     html = html.replace('position:fixed;top:0;left:0;right:0;', 'position:static;')\n","firstChangedLine":317},"isError":false,"timestamp":1780219938332}}
{"type":"message","id":"d91ac3f3","parentId":"17762dc8","timestamp":"2026-05-31T09:32:28.953Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_YiPNiHEilVyssci4pdoYyClc|fc_0f029265420fe376016a1c00264f2c819199ae1a46843e328b","name":"bash","arguments":{"command":"python -m py_compile /home/sebas/work/projects/rss-offline/build.py && python - <<'PY'\nimport importlib.util\nfrom bs4 import BeautifulSoup\nfrom pathlib import Path\nspec=importlib.util.spec_from_file_location('rssbuild','/home/sebas/work/projects/rss-offline/build.py')\nmod=importlib.util.module_from_spec(spec)\nspec.loader.exec_module(mod)\nentries=[\n('https://github.blog/news-insights/company-news/still-a-developer-just-outside-our-latest-github-shop-collection-is-here/','c05e51dd412952e4'),\n('https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers','c22ede05fdcde865'),\n('https://simonw.substack.com/p/i-think-anthropic-and-openai-have','f419e05b4804e437'),\n('https://tailscale.com/changelog/#2026-05-29-service','04009026a7f5cca2'),\n('https://world.hey.com/dhh/basecamp-five-8fcfd2ef','8ce8fe6b9afb1b1c'),\n]\nfor url,key in entries:\n    soup=BeautifulSoup(Path(f'/var/www/rss-offline/article-{key}.html').read_text(),'html.parser')\n    entry={'link':url}\n    picked=mod.pick_print_root(mod.isolate_anchor_section(soup,entry), entry)\n    txt=len(picked.get_text(' ',strip=True))\n    print(key, txt)\nPY","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1408,"output":369,"cacheRead":102400,"cacheWrite":0,"totalTokens":104177,"cost":{"input":0.00352,"output":0.005535,"cacheRead":0.025599999999999998,"cacheWrite":0,"total":0.034655}},"stopReason":"toolUse","timestamp":1780219938335,"responseId":"resp_0f029265420fe376016a1c002266a881918ddeb3486369baf3"}}
{"type":"message","id":"bbe62afd","parentId":"d91ac3f3","timestamp":"2026-05-31T09:32:29.906Z","message":{"role":"toolResult","toolCallId":"call_YiPNiHEilVyssci4pdoYyClc|fc_0f029265420fe376016a1c00264f2c819199ae1a46843e328b","toolName":"bash","content":[{"type":"text","text":"c05e51dd412952e4 2293\nc22ede05fdcde865 4472\nf419e05b4804e437 40223\n04009026a7f5cca2 339\n8ce8fe6b9afb1b1c 3086\n"}],"isError":false,"timestamp":1780219949906}}
{"type":"message","id":"fc942de5","parentId":"bbe62afd","timestamp":"2026-05-31T09:32:33.332Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Updating tasks for Tailscale**\n\nI think I need to focus on possibly using Tailscale 339, as it seems like a solid choice, probably because it's more isolated with just one entry. I should report this somehow and make sure there aren’t any temporary artifacts floating around. It's really important to stay organized, so I’ll update my to-do list as a next step. Let’s make sure I don’t miss anything!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1c002f4e2881919e4ba6f2f87ded0e\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAAxkIpOBy9hmhmfK6pF6--qgDMfSEoA9oIKRaFARpzR6KczKScQ_0zaTGGaZAKjaG3ZX89o1huJkFPipXYH3j6wpb2DLfnNsxoI9AHMEOZU8yXiHnUDCg0kpzOzLxXev00m-Iv5RqOnH_8eVSEIkUKxlfHFGATagXI1QYxA5oIMkHR7z3jO0pOHm1nOzVneV2zfuiX8e-_eleMv--c5AsUrHhn6ZlSf1z-eBsdJeJa9oe810hZdEewu4X6LXxKJRWrK793KrvrxX7ag6ZA8JvyQK0IiTbqtiQB998lfz-4Ro3oH4ox0HgEJJBic15wEplGAoFfS6335CMNYjeQt1VVhnQvtxnvRZ-F_UaLiS12fLMaa6X6fnqU4BJyA-eIE741lGH1G2GS7x_JwyG0E4SRBb5-GBXbwxwQGS4cexlfopyOzb5pImAS80by6b5ZXG3mNrdl3bmCklXh8zjY8kcUetwxr1p8UvMmaApBCB5Nx6FrtPiKYLHlXMh0KvUM4SZR5yz43v6I_W3eWIw4awCvBfDkih-1UQckM9ogGq__gxVwxVoBvS_yFqtBjSjfS3UPOPiJzxxOXS6cTEHvDCvd6r5_jwpy6UQzLXvOjP3rASlpE8r1Kp4R-keRDMvxvSXNkEjbxyYe6Hpsh16S5ISBFKOXMKvlbSeMM49yvHMvF4l2ClQqQ0ztyDdPnkIZjw8O-y8PQFC_U0NyPivzY97jds0-l2h8rPWqVTw-E_QtSz6E_RC_B8hxVsF7soe_HGIgz45fVDlfctZzGcRpJKQL5elnp8qm39Bu6U5XRgaH-pBVyBekhrUofe5i2zNA-aMqXO4TPLeM_V1a_AEhnUrkXp7Xy9e1a7QrGFNyY90WosAcSSYzaPHhxp20DSyhwyDaPiREC2pqbm2FzTEbjeaPT-CuxzCkEOk-vV47dYbWbVABCzdKMhauJkGvG-GSD5Fp7j-qqS3TFg-YQbsd19LhdvbzdylCXFV6JfIIqunIfYFHXkT2EW8v93XdtbEHZkxm-Ylz8BForI4rRPLqHYVLVLUga1EoIuXpR-rlK7rHXQEmynMXBVZbAWLaV2ZgQTpsM\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Updating tasks for Tailscale**\\n\\nI think I need to focus on possibly using Tailscale 339, as it seems like a solid choice, probably because it's more isolated with just one entry. I should report this somehow and make sure there aren’t any temporary artifacts floating around. It's really important to stay organized, so I’ll update my to-do list as a next step. Let’s make sure I don’t miss anything!\"}]}"},{"type":"toolCall","id":"call_5x0wDfs1gpJRsqRNWQooYJPk|fc_0f029265420fe376016a1c00311f1c81919a4abe12eed22618","name":"todo","arguments":{"action":"update","id":4,"status":"completed"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":830,"output":57,"cacheRead":103424,"cacheWrite":0,"totalTokens":104311,"cost":{"input":0.002075,"output":0.0008550000000000001,"cacheRead":0.025856,"cacheWrite":0,"total":0.028786}},"stopReason":"toolUse","timestamp":1780219949908,"responseId":"resp_0f029265420fe376016a1c002df914819195e86d743a555606"}}
{"type":"message","id":"d565e9a1","parentId":"fc942de5","timestamp":"2026-05-31T09:32:33.333Z","message":{"role":"toolResult","toolCallId":"call_5x0wDfs1gpJRsqRNWQooYJPk|fc_0f029265420fe376016a1c00311f1c81919a4abe12eed22618","toolName":"todo","content":[{"type":"text","text":"Updated #4 (pending → completed)"}],"details":{"action":"update","params":{"action":"update","id":4,"status":"completed"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"completed","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"},{"id":3,"subject":"Tighten rss-offline PDF extraction for noisy blogs","status":"completed","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","owner":"agent"},{"id":4,"subject":"Audit all recent rss-offline feed domains for PDF targeting","status":"completed","description":"Inspect current entries by domain, identify article-body selectors per host where useful, extend builder host-specific print root targeting, and clean any temporary artifacts. No rebuild unless needed for validation-only local parsing.","activeForm":"auditing rss-offline domains","owner":"agent"}],"nextId":5},"isError":false,"timestamp":1780219953333}}
{"type":"message","id":"4fcb611d","parentId":"d565e9a1","timestamp":"2026-05-31T09:32:38.606Z","message":{"role":"assistant","content":[{"type":"text","text":"Hecho.\n\nMetí foco por host en el builder.\nNo rebuildeé.\nNo dejé basura.\n\nCubrí:\n- `github.blog`\n- `www.digitalocean.com`\n- `*.substack.com`\n- `tailscale.com`\n- `gomakethings.com`\n- `leadershipintech.com`\n- `martinalderson.com`\n- `openclaw.ai`\n- `world.hey.com`\n- `mariozechner.at`\n\nCambio clave:\n- para URLs con `#fragment` ahora primero:\n  - `isolate_anchor_section()`\n- después:\n  - `pick_print_root()`\n\nEso importa mucho para `tailscale.com/changelog/...`\nporque si no podías perder la entrada correcta antes de recortar.\n\nResultado esperado:\n- menos nav/sidebar/newsletter/comments\n- más cuerpo real del post\n- mejor especialmente en:\n  - GitHub\n  - DigitalOcean\n  - Substack\n  - Tailscale changelog\n\nNo toqué salida generada.\nVa a aplicar en el próximo build.","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1c003275788191a212d55d5e285e97\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":393,"output":224,"cacheRead":103936,"cacheWrite":0,"totalTokens":104553,"cost":{"input":0.0009825,"output":0.00336,"cacheRead":0.025984,"cacheWrite":0,"total":0.0303265}},"stopReason":"stop","timestamp":1780219953335,"responseId":"resp_0f029265420fe376016a1c00316c54819191968832aee43509"}}
{"type":"message","id":"2e703718","parentId":"4fcb611d","timestamp":"2026-05-31T09:38:41.940Z","message":{"role":"user","content":[{"type":"text","text":"[telegram] bueno ahora me da curiosidad, rebuildea el ultimo que ahora pesa 8.1"}],"timestamp":1780220321939}}
{"type":"message","id":"93901c6f","parentId":"2e703718","timestamp":"2026-05-31T09:38:46.697Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1c01a4cd048191b86d46a31776d19d\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAGmD3OaTaB3m7LG7t-ezxNyqfTe1Enq1l8v9cZ9dphs9g5JTqHze3J50AkSh2PdVMCDhckyftFNay3nWIb5mz3Nu0f83vxZgNQB_8SKtB260q1_Ms4s-n3tFpnfv9eybKpwq8_FzJkLHmpEuyfkZ6Td6SZxGM8Nd-y939Ddxiev4gCrNnmn73vOgXyIseQlfejeRDQYTzBblz6CrYD8iVO4gGx_jq1pg-IJ2e_uxWx5Mdpxl6qVrQmOylHLxcTWKoDXGJXmCNFblqNqwG3zKrKEYGTh0iM-Csv32pYOjWzsox6KpIKhaHa1Reklyrob4nXlT-4YcyKnF9o7kYrKqlWO4gTYmYcVRf4HMnjZTO7SYyhoNacrtWXYMRaXmv7ZJwoPktKcGd_dcFpAgd_wbxSw2PIqEIjtlvBocIVokCeHNdMY_hbTWH6oFP9mPkOEABWlHmPSKzbGZBP668Dj0PAIccCwRpY-1lQCRyU9MMYTDtwGQzUO2U5aN7sC-pwX9Xl-8fnkFD6Z6ochZ0J4qoeE7ynQBF_xHvyykgeKagzytb5dGwkzlZsJrA9Bf0IRqEFY_ppzkkxi54Rv5JuUeZ-X0MySLQ-ZqH0Agz5cDykTp7D4H4vVBcJVcy0_eN-YY3i5Elms_p3O6s_6exqr7CvEBxLZY6hgCSDxv9HEElMlmUmEQNl5GdQ-Cdo4-akXU7Wm7w66OwksMXNT_62alabyYXcs3WucCjOIquQevds0np48im8DNUatUuKmQ4jKa2JZDf6BgtbvzhcvmCPU2DxHJe7oAnNt1WQK1A63A82UJlmTKehAg9GbRzBOCeE9o29UY2Ch0WY3UPXuvbQuzXwNK33M_A9B4W0EZqMJR34ygZROPHBJdevzFkl4h60VYlBWa1aIAyb6hXYiD-lcr4mQYXzTsLBN2GpbDLf_GFmW3vMqy5Qfb8FsA35edeS1U6RfJ-gFfmZTS2E6yQNJP6lWITPS0TALXB_YCw9qN_pX42o=\",\"summary\":[]}"},{"type":"toolCall","id":"call_nWoztR7wxjqVzfTuXswnKbIG|fc_0f029265420fe376016a1c01a639408191a8b0c75eb8a4ff26","name":"todo","arguments":{"action":"create","subject":"Rebuild rss-offline latest PDF after targeting improvements","description":"Run rss-offline builder so the newest PDF is regenerated with the new host-specific targeting, then report the resulting latest PDF size.","activeForm":"rebuilding rss-offline pdf","status":"in_progress","owner":"agent"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":5115,"output":96,"cacheRead":98816,"cacheWrite":0,"totalTokens":104027,"cost":{"input":0.0127875,"output":0.00144,"cacheRead":0.024704,"cacheWrite":0,"total":0.0389315}},"stopReason":"toolUse","timestamp":1780220321942,"responseId":"resp_0f029265420fe376016a1c01a26168819180ca8f7d85e49715"}}
{"type":"message","id":"83b4f132","parentId":"93901c6f","timestamp":"2026-05-31T09:38:46.698Z","message":{"role":"toolResult","toolCallId":"call_nWoztR7wxjqVzfTuXswnKbIG|fc_0f029265420fe376016a1c01a639408191a8b0c75eb8a4ff26","toolName":"todo","content":[{"type":"text","text":"Created #5: Rebuild rss-offline latest PDF after targeting improvements (pending)"}],"details":{"action":"create","params":{"action":"create","subject":"Rebuild rss-offline latest PDF after targeting improvements","description":"Run rss-offline builder so the newest PDF is regenerated with the new host-specific targeting, then report the resulting latest PDF size.","activeForm":"rebuilding rss-offline pdf","status":"in_progress","owner":"agent"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"completed","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"},{"id":3,"subject":"Tighten rss-offline PDF extraction for noisy blogs","status":"completed","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","owner":"agent"},{"id":4,"subject":"Audit all recent rss-offline feed domains for PDF targeting","status":"completed","description":"Inspect current entries by domain, identify article-body selectors per host where useful, extend builder host-specific print root targeting, and clean any temporary artifacts. No rebuild unless needed for validation-only local parsing.","activeForm":"auditing rss-offline domains","owner":"agent"},{"id":5,"subject":"Rebuild rss-offline latest PDF after targeting improvements","status":"pending","description":"Run rss-offline builder so the newest PDF is regenerated with the new host-specific targeting, then report the resulting latest PDF size.","activeForm":"rebuilding rss-offline pdf","owner":"agent"}],"nextId":6},"isError":false,"timestamp":1780220326698}}
{"type":"message","id":"b2c72e8f","parentId":"83b4f132","timestamp":"2026-05-31T09:38:53.331Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_IaIfkzjbCeKBchm9sGtoKemp|fc_0f029265420fe376016a1c01a98164819184098ee665541278","name":"bash","arguments":{"command":"cd /home/sebas/work/projects/rss-offline && before=$(ls -1t /var/www/rss-offline/rss-offline-*.pdf 2>/dev/null | head -1) && echo \"before:$before\" && sudo ./build.py >/tmp/rss-offline-build.out && after=$(ls -1t /var/www/rss-offline/rss-offline-*.pdf | head -1) && echo \"after:$after\" && stat -c 'size_bytes:%s' \"$after\" && python - <<'PY'\nfrom pathlib import Path\np=Path(sorted(Path('/var/www/rss-offline').glob('rss-offline-*.pdf'), key=lambda x: x.stat().st_mtime, reverse=True)[0])\nprint('size_mb:%.1f' % (p.stat().st_size/(1024*1024)))\nPY && tail -20 /tmp/rss-offline-build.out","timeout":1200}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":630,"output":213,"cacheRead":103424,"cacheWrite":0,"totalTokens":104267,"cost":{"input":0.0015750000000000002,"output":0.003195,"cacheRead":0.025856,"cacheWrite":0,"total":0.030626}},"stopReason":"toolUse","timestamp":1780220326701,"responseId":"resp_0f029265420fe376016a1c01a6c81c8191868e0c5d88b09818"}}
{"type":"message","id":"e27661d9","parentId":"b2c72e8f","timestamp":"2026-05-31T09:41:27.766Z","message":{"role":"toolResult","toolCallId":"call_IaIfkzjbCeKBchm9sGtoKemp|fc_0f029265420fe376016a1c01a98164819184098ee665541278","toolName":"bash","content":[{"type":"text","text":"/bin/bash: line 5: warning: here-document at line 1 delimited by end-of-file (wanted `PY')\nbefore:/var/www/rss-offline/rss-offline-2026-05-31.pdf\nafter:/var/www/rss-offline/rss-offline-2026-05-31.pdf\nsize_bytes:6840244\n  File \"<stdin>\", line 4\n    PY && tail -20 /tmp/rss-offline-build.out\n        ^\nSyntaxError: invalid syntax\n\n\nCommand exited with code 1"}],"details":{},"isError":true,"timestamp":1780220487766}}
{"type":"message","id":"2c4bbc98","parentId":"e27661d9","timestamp":"2026-05-31T09:41:32.686Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Inspecting build success**\n\nI need to check if the build succeeded before dealing with any syntax errors. It's probably a good idea to look at the size and logs separately to get a clearer picture. By making sure everything is in order before diving deeper, I can avoid confusion down the line. Let’s focus on gathering all the necessary information first, that way, I can tackle any issues more effectively!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1c0249560881918f8b12d6b29d6260\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAJMJpscdbxi3XL3KAh1o7-D_stlWcg6fNkNdVDmPDrXUUfqQXJnwY5sHyQ_8nqRgM_rgRyPGLvJdnjoP3DiDoPcXHEOq_Hx2Yzk_3w95kCaQvo6UbWYurRn0d3KQqQYse5M_X4X7iDH1pGVFrLfS753kP03BwzShl7G1reCrRa8TEpn5602sQcHjuONUjfebA2QSOrYr7HhvmzsKvCF0MlQ-LsNx-WUc8Py7gOSdBic-VmO02B7qt1az8g0lsqPGRMw4qNXZ_X_rKRvn5AknPQ_oXGX7HsUSVHa8MnkWsi_IYaAj3bpkcJ4EHYLgPO_03rocgNwmHZ073EjDrTBpBTZChv-TdyhNz1smmdIXIdKT9zI5Lfq7fpQwDmP6HFRXym3WFda6EOjOBEfRtGHz3Yv0zeGSX2no1yA3hN0v3EOrZivPA4mPK_a6v4T799IC1gy6dXZxs9HCWxxM6agKyb4q5Ywb6IWzMuAt7Nr7FfUYKDKYRT17VO2FnlFiHJW6kmLtKumCj4dRje23wP6ybBGW8_cEs2Z8BhYwF0rEe96tYnNKjw71LzKOXl3XTQi7uxRZNGvARrd_2JGIi90LI4K_aoOKAdzwMHb9sgLZiYTkNq3FNse6-2CmIHodP_t-ZzPD0qikxIy6OzjqE9B25otKoUNuCjwFv4Ad0a5J3Acl0MFddKE9EvL5_3NyvT6r8VU_1tmckeE3iW5VDLXpR3Q3Mmd-popruP-tdJyx5qwBQ5fRBdFqbH_tBEzQt67CMnopYp6iCVD-ZW5MJQKr-qxOjPCC-DQ8Y2F8WHVsJVNQ0LI4YKasITTY45XrQEbTOBAn7FGbBFXByfUfJw7JsT1lS0YRXKkHYy7E8IyM27YVJLc9Uib6z25lh8Z6xWFAqixUCfGk2guChmUvAjPxW1AWkCq9aYI56ff9ejAUOg_cqwmwyVY3s0LcQRv7jR0Begdv_sYa84vQ0VTLl3v1K2-7vLMCg9Qksg_v4H1WCI2Byttyi3E3cUq4oBerA1RAlfT_6rsVj--3SrROfDTXfoyXA==\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Inspecting build success**\\n\\nI need to check if the build succeeded before dealing with any syntax errors. It's probably a good idea to look at the size and logs separately to get a clearer picture. By making sure everything is in order before diving deeper, I can avoid confusion down the line. Let’s focus on gathering all the necessary information first, that way, I can tackle any issues more effectively!\"}]}"},{"type":"toolCall","id":"call_5G9OsSfleGqqyw6dtzZCmFO4|fc_0f029265420fe376016a1c024c9f78819199b943dcf0994bb3","name":"bash","arguments":{"command":"stat -c 'latest:%n size_bytes:%s' /var/www/rss-offline/rss-offline-2026-05-31.pdf && python - <<'PY'\nfrom pathlib import Path\np=Path('/var/www/rss-offline/rss-offline-2026-05-31.pdf')\nprint('size_mb:%.1f' % (p.stat().st_size/(1024*1024)))\nPY","timeout":30}},{"type":"toolCall","id":"call_kvqCIJ97rCyLn2OGCNIZpOTL|fc_0f029265420fe376016a1c024c9f888191be134e185a5603a3","name":"bash","arguments":{"command":"tail -20 /tmp/rss-offline-build.out","timeout":30}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":450,"output":176,"cacheRead":103936,"cacheWrite":0,"totalTokens":104562,"cost":{"input":0.0011250000000000001,"output":0.00264,"cacheRead":0.025984,"cacheWrite":0,"total":0.029749}},"stopReason":"toolUse","timestamp":1780220487768,"responseId":"resp_0f029265420fe376016a1c0247d4ac81919af5e2a0431307f9"}}
{"type":"message","id":"cc466307","parentId":"2c4bbc98","timestamp":"2026-05-31T09:41:32.724Z","message":{"role":"toolResult","toolCallId":"call_5G9OsSfleGqqyw6dtzZCmFO4|fc_0f029265420fe376016a1c024c9f78819199b943dcf0994bb3","toolName":"bash","content":[{"type":"text","text":"latest:/var/www/rss-offline/rss-offline-2026-05-31.pdf size_bytes:6840244\nsize_mb:6.5\n"}],"isError":false,"timestamp":1780220492724}}
{"type":"message","id":"877cf17b","parentId":"cc466307","timestamp":"2026-05-31T09:41:32.724Z","message":{"role":"toolResult","toolCallId":"call_kvqCIJ97rCyLn2OGCNIZpOTL|fc_0f029265420fe376016a1c024c9f888191be134e185a5603a3","toolName":"bash","content":[{"type":"text","text":"OK simple https://github.blog/open-source/building-githubs-next-chapter-in-accessibility/\nOK simple https://leadershipintech.com/newsletters/2282-recognize-your-management-wins\nOK simple https://tailscale.com/changelog/#2026-05-21-client\nOK simple https://tailscale.com/changelog/#2026-05-21-service\nOK simple https://github.blog/security/investigating-unauthorized-access-to-githubs-internal-repositories/\nOK simple https://supernuclear.substack.com/p/case-study-tamera\nOK simple https://www.digitalocean.com/blog/inference-router-architecture\nOK simple https://tailscale.com/changelog/#2026-05-20-service\nOK simple https://simonw.substack.com/p/the-last-six-months-in-llms-in-five\nOK simple https://tailscale.com/changelog/#2026-05-19-service\nOK simple https://github.blog/news-insights/product-news/take-your-local-github-sessions-anywhere/\nOK simple https://leadershipintech.com/newsletters/2281-how-to-stay-resilient-in-a-difficult-job\nPDF_OK https://mariozechner.at/posts/2026-05-30-shitty-robot\nPDF_OK https://www.digitalocean.com/blog/ai-disruptors\nPDF_OK https://supernuclear.substack.com/p/the-half-assed-potluck-and-other\nPDF_OK https://gomakethings.com/articles/how-to-redirect-if-a-page-doesnt-exist-with-apache/\nPDF_OK https://simonw.substack.com/p/i-think-anthropic-and-openai-have\nPDF_OK https://tailscale.com/changelog/#2026-05-29-service\nPDF_OK https://martinalderson.com/posts/whats-going-on-with-gemini/?utm_source=rss&utm_medium=rss&utm_campaign=feed\nPDF_OK https://www.digitalocean.com/blog/digitalocean-opencode-inference-routers\n"}],"isError":false,"timestamp":1780220492724}}
{"type":"message","id":"8a566ea2","parentId":"877cf17b","timestamp":"2026-05-31T09:41:35.201Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_Ec4AwQiBfTTkCFRFIk0I3NIR|fc_0f029265420fe376016a1c024eb5b88191b80c713e9f91e959","name":"todo","arguments":{"action":"update","id":5,"status":"completed"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1099,"output":25,"cacheRead":103936,"cacheWrite":0,"totalTokens":105060,"cost":{"input":0.0027475000000000004,"output":0.000375,"cacheRead":0.025984,"cacheWrite":0,"total":0.0291065}},"stopReason":"toolUse","timestamp":1780220492726,"responseId":"resp_0f029265420fe376016a1c024cc95c8191981a8b47cdee5029"}}
{"type":"message","id":"f4589c4c","parentId":"8a566ea2","timestamp":"2026-05-31T09:41:35.203Z","message":{"role":"toolResult","toolCallId":"call_Ec4AwQiBfTTkCFRFIk0I3NIR|fc_0f029265420fe376016a1c024eb5b88191b80c713e9f91e959","toolName":"todo","content":[{"type":"text","text":"Updated #5 (pending → completed)"}],"details":{"action":"update","params":{"action":"update","id":5,"status":"completed"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"completed","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"},{"id":3,"subject":"Tighten rss-offline PDF extraction for noisy blogs","status":"completed","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","owner":"agent"},{"id":4,"subject":"Audit all recent rss-offline feed domains for PDF targeting","status":"completed","description":"Inspect current entries by domain, identify article-body selectors per host where useful, extend builder host-specific print root targeting, and clean any temporary artifacts. No rebuild unless needed for validation-only local parsing.","activeForm":"auditing rss-offline domains","owner":"agent"},{"id":5,"subject":"Rebuild rss-offline latest PDF after targeting improvements","status":"completed","description":"Run rss-offline builder so the newest PDF is regenerated with the new host-specific targeting, then report the resulting latest PDF size.","activeForm":"rebuilding rss-offline pdf","owner":"agent"}],"nextId":6},"isError":false,"timestamp":1780220495203}}
{"type":"message","id":"00078cad","parentId":"f4589c4c","timestamp":"2026-05-31T09:41:37.609Z","message":{"role":"assistant","content":[{"type":"text","text":"Sí. Rebuild hecho.\n\nÚltimo PDF:\n- `rss-offline-2026-05-31.pdf`\n\nTamaño nuevo:\n- antes: `8.1 MB`\n- ahora: `6.5 MB`\n\nBajó bastante.\nSe ve que el targeting nuevo limpió bastante basura.","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1c0250538c8191873bb527022050f4\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":118,"output":66,"cacheRead":104960,"cacheWrite":0,"totalTokens":105144,"cost":{"input":0.000295,"output":0.00099,"cacheRead":0.02624,"cacheWrite":0,"total":0.027525}},"stopReason":"stop","timestamp":1780220495205,"responseId":"resp_0f029265420fe376016a1c024f44488191ba83e53c7fc6709a"}}
{"type":"message","id":"72f157f1","parentId":"00078cad","timestamp":"2026-05-31T09:42:46.526Z","message":{"role":"user","content":[{"type":"text","text":"[telegram] cool, ahora tambien miras los feeds de tailscale que traen changelog históricos?"}],"timestamp":1780220566525}}
{"type":"message","id":"d8ec17ec","parentId":"72f157f1","timestamp":"2026-05-31T09:42:52.727Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Clarifying Tailscale Feeds**\n\nI think I should ask the user for clarification to make sure I understand what they're asking about. It seems they might be noticing that Tailscale feeds show historical changelog items. I can respond concisely, saying \"yes,\" and explaining the current behavior based on entries. There are recent Tailscale changelog items within the DAYS window, but it doesn’t fully cover everything historically beyond that cutoff. If they want me to inspect further, I can definitely agree to do that.","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1c02981a3c8191b99ce7aee7ac6728\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAKc--9FiFGpoHpJhp9ASaVNP7dXpZwemc1X84rYmDe9JCBRm5yIcLz4wCQfxVTdBIzItcCqYAfM_Vief2gF6NRdl9jVGJo-fCoONEfSsUL0QYRP7v4Zf9-b3mQQgvhwJOUxHLdIgEVEZByE7l0Wwc5RpUSt6gYEL3Vl3CF2W80oHklH-JNuKQCpB8CdFxkrMQBptb4X6yQxuZgrxEasKF-vApB-Xct-_jYtTYBdHKqVcb1BRp72UXJZAgPZcX7KidDQLP51BKrUd14o4rL0Ib-ZFgu2_mXuvsXsf_9Wvj_e-IH47GTWhR2AnMp97Nh3GL8sO-_PuwCBrzpukbZKVSSlllScuJlU2NUVY2Pfwjhn3rN3M7GgpZuu5gaekcgLZab0c6FVQnx_IUuIzdVnUIYgzg2YmJcq_xRZgwu3AE_uW3vusG_9T_ti0avqn31AF67V-_AD-V7YLoowmfJs6bkZwlQj0Tei5lMuWxYx5F4WIhPOyERLkvDnfoSEPeUMZOkzDjC1Fh4bBs2ql3tHvFnLrgPyYHWjXpUZ_h6YH_dD3AesqchYzgMgk4uH1EYROD4PgYq0DEcfFKYUtHPsouUzBisMseZkbo0LW0q0I-v1Qg_TjDdy8epnnalWEtwdzqR01X8m_wmO4Pb5LL-nCYxEgEyy_vOmP00DlZFXY-oL30gUAv2cPoB-mg2uGaI9C1AKYUy6oXbTVBhZsc7-rpBRXtZeHwztQBjFKNC1rpoQ6PkTJd_gGe4sbwmFxjXTg4c8wdcn4zmiG_V64DipovH6_I-aZUhUAd7SpEkqZ2SQfWd17jFj-qfVtYJIN00Llo-X9XscvIbajlbPzrP1Ek4RRGcu8oTpVHhTmT17hsWyYfKna6TQf5R_8HTW34yzld2pueh9DHxa-DJHzFF9FbqLX7QfluKdGV_bHvW4JiaxWtVXwR4OzS7f17pzdfX-m3Pf0-r9TRYMvKifgbzjHd2kWt1VS1KGQ-AqGJHoSe3-qm5-8eKhwJ-C62aCDVhOZrVbzZqkd5cFtg2sVVEsWKxdeelqEtUEfTROo92g8Wc-NzbeWnFczJWHEW9sHx3-qhxAcfman4sPMRDm1y6TlOIE85pgnYwbJyuw92s3E7V81njdopFsmfHyV-O28QdQEDpo-QVKXsKwKyCHik1qC1E18XKoWVQiGoGigK8bMsfa3PeZA93O3xaFle7-MVB8aWwIhNaJXmeOwf4C2q-K3IWfjQIvAuHcVH7uPQwEApVzrGP9I_-p_r2UunxlEoUS8PKpDy13VJmYh8fVe6JVv97ize1t5FAM-02RW1d7oqRLXWY4wvMzZEV2wTVQXJWlDyKgm55qgGRQ1oBHetOzvck9-VHqsCLhVnSBCWQUFjEhZlajiJtYJY-MIMX7cRNZaT_r3CQT4n3R0Xzyten7kfauPAYNq9lRTCb6wrMTELQz69s=\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Clarifying Tailscale Feeds**\\n\\nI think I should ask the user for clarification to make sure I understand what they're asking about. It seems they might be noticing that Tailscale feeds show historical changelog items. I can respond concisely, saying \\\"yes,\\\" and explaining the current behavior based on entries. There are recent Tailscale changelog items within the DAYS window, but it doesn’t fully cover everything historically beyond that cutoff. If they want me to inspect further, I can definitely agree to do that.\"}]}"},{"type":"text","text":"Sí.\n\nTailscale mete muchas entradas de changelog:\n- `client`\n- `service`\n- con links tipo `#2026-05-29-service`\n\nNo parece “histórico infinito”, pero sí:\n- trae varias entradas viejas dentro de la ventana\n- y como cada anchor cuenta distinto, ensucia bastante el pool\n\nHoy está limitado por:\n- `DAYS=14`\n- dedupe por URL completa\n\nO sea:\n- no entra todo el pasado\n- pero sí bastante changelog reciente/histórico dentro de esos 14 días\n\nSi querés, siguiente paso útil:\n- capar Tailscale a:\n  - solo 1 entry por día\n  - o solo 1 por feed\n  - o excluir changelog y dejar solo posts normales\n\nEso te baja ruido fuerte.","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1c029bfee0819190403130dafb6ad0\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1707,"output":249,"cacheRead":103424,"cacheWrite":0,"totalTokens":105380,"cost":{"input":0.0042675000000000005,"output":0.003735,"cacheRead":0.025856,"cacheWrite":0,"total":0.0338585}},"stopReason":"stop","timestamp":1780220566532,"responseId":"resp_0f029265420fe376016a1c02969e1c8191b271db842992be1c"}}
{"type":"message","id":"8c41289e","parentId":"d8ec17ec","timestamp":"2026-05-31T09:44:20.416Z","message":{"role":"user","content":[{"type":"text","text":"[telegram] en https://ballbox-first.emperor-ratio.ts.net/rss-offline/rss-offline-2026-05-29.pdf vi hasta 2021 changelog. suena a infinito"}],"timestamp":1780220660415}}
{"type":"message","id":"19ae98db","parentId":"8c41289e","timestamp":"2026-05-31T09:44:26.753Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Considering PDF Fragment Issues**\n\nI need to provide a concise answer about the fragment issue in an old PDF, possibly explaining it before presenting the fix. I'll want to inspect the feed, maybe checking if the anchor content captures the whole changelog page with historical entries. The whole page is likely captured due to the main container on Tailscale being intact before the fix was applied. No tools are required for this process.","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1c02f63f38819199e8c0bb4dbade0e\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAL5NTnJ27FNRxGzIkqg9UWuYr9PiU3LmGuJCm-g3UPtcVEF0HzOds5gm-TydhRNXYvvu7V8lD39cqqoDVcifvblALmqYQskXm_fBtQ70Egavr4jQFZMSX5rtFeXkcHKEhLqgUER35bHv9DBpiKvfS3IRkYv8OmDboO4CaGG2nfWMCcvSnLyDBobQjGvsflO4lnDUQok7DRA51o6x-rGh5hL_p-4lTy7vDoVo9puy9agO1EcdRenXLH2uhsLelz8SL7X7X5ObXqfuZCtS-wizwQfY_lwiN65kePHjQk-g5DFGgvC8BWvo4uouXmXR_QPqWnKcB5fWmwqHAdJ-K0sYs7BMH5zdLtvXzUiCd_LS0fp3Ek7dgSePRJxB8M-oclkv-a_uQxADBpSnbXEbZFzPbnxnLjxidPLd1FDuywucvPLsSwXwrrjlVLk-RkZdr4np6kexaqbCL-zQQbDwrNnHGgbRzUl53sNUshu6VIrcnIoSjTZKWcqLBPbB5dMAW4GbcTC7QXSwYO6aor5-tlCx_phIydbmzTeJ530wsGMsWGwM8wX7gVNBSx2m_0AizS_2OFtFVxO0YDxouMKXUOZGxwCS2BUGL0GjujKRQdH3ny2S5oQmd8aiq8KXF-H_2mVAD-u2ttvN_rnlWOgKpj2tjA4-K8RAkFZ5pt0JkZ0CIdTcmdfQcK_nmp3Pl63sc8PmUw0RxXwJW3WyQGJyepZ5E6kjVb9k1G_nLLWVHJb5_lLsU7Ac0JexuXyOIsyp7JoqgO5yvXwWWjzK5oM5JwWkJ8R8YWwW67ii_404C0IOMijZfPVFSe2Bj3V9Tbe8hBYUj2hD3bM62yJDAciGRyfJlUjrUpbheGtTjlyOwcQ63-FJWSb9QjSv5mnHxvZSp44yN_EHKd3U_ZN7VNyjbFG19_TAF_4AoA6j4PABoCPb7ao_VVFUpmCPuKeQ9Xg-4lL7MY4VVVlCsNzsVCJRqyNuEFDgAMShNBR6QCRuECdCS9LSEOJG0PNuZVypSTd91aYkqhy_eEiLKAXij7Cz9k0k-Qyq0magN7fxWqcVTe1oECVg47wAZGzPI0elJav1eI1dcQg98AxRdsFcPTuarLU7L8e_RFe_0hfYl-SMGb6RXHoGJfuyoqepBQcCqVq0-QVRS70gxqZjIvjk3Ojmk_B4IaT6tsZkwbxj6o1jbZGnxgr14AmpaaNqzAGyPUsoYNr0fdyJnyXQtqHC57INFKjv8HBrf3l_VuFasWg_Sdi8EpOXKdHw8fnWl_8AchKp_ShsMioPLbQwJoFtTOC_2Mwbrly-IE-vEj7Motny7A6NxeID3WfSCxYl9xb2rIRKAGPh7l5\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Considering PDF Fragment Issues**\\n\\nI need to provide a concise answer about the fragment issue in an old PDF, possibly explaining it before presenting the fix. I'll want to inspect the feed, maybe checking if the anchor content captures the whole changelog page with historical entries. The whole page is likely captured due to the main container on Tailscale being intact before the fix was applied. No tools are required for this process.\"}]}"},{"type":"text","text":"Sí. Entonces era eso.\n\nNo era el feed trayendo 2021.\nEra el capture de Tailscale.\n\nQué pasaba:\n- el link del changelog va a un `#fragment`\n- pero la copia/PDF estaba agarrando mucho de la página entera\n- esa página incluye histórico largo\n- por eso terminabas viendo 2021 y parecía infinito\n\nEso encaja perfecto con lo que ya corregí:\n- primero aislar el anchor del changelog\n- después recortar al root útil\n\nO sea:\n- antes: podía entrar histórico enorme\n- ahora: debería quedar mucho más acotado a esa entrada\n\nSi querés, próximo ajuste extra para Tailscale:\n- además de recortar mejor,\n- limitar cuántos changelog entries de Tailscale entran por build\n\nEso baja ruido incluso si el capture ya quedó mejor.","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1c02f9b05c8191b6d537c87c64e405\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":389,"output":243,"cacheRead":104960,"cacheWrite":0,"totalTokens":105592,"cost":{"input":0.0009725000000000001,"output":0.0036450000000000002,"cacheRead":0.02624,"cacheWrite":0,"total":0.0308575}},"stopReason":"stop","timestamp":1780220660420,"responseId":"resp_0f029265420fe376016a1c02f47eac8191903fe46299d62343"}}
{"type":"message","id":"6807a0c4","parentId":"19ae98db","timestamp":"2026-05-31T09:48:43.077Z","message":{"role":"user","content":[{"type":"text","text":"[telegram] fijate si rebuild de solo el feed de tailscale de ese dia reduce el contenido bien ahora (si podes simple, sino espero a otro feed y veo yo)"}],"timestamp":1780220923076}}
{"type":"message","id":"147751b7","parentId":"6807a0c4","timestamp":"2026-05-31T09:48:48.887Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"**Simulating PDF generation**\n\nI’m thinking about simulating specific PDF entry generation without having to rebuild everything. I wonder if I can use existing functions by importing them. It looks like I might not need a big edit. I could test generating a printable HTML/PDF for the Tailscale entry from that day using the Python import build module. I'll check the output text length or page count, and maybe generate a temporary PDF for inspection. Cleanup for the temp files will also be necessary. Let’s keep it simple!","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1c03fc8b54819190bd764d1d3af167\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAQALClDeFeUhTjotcYwL9o9NstXxA5cd-eJZ6PBW6H7spgoro0jYiVhzAaqGgYveKJsmZ33Q3KamZ27_rERTXiIauiOreK5_jVIULIqLOAh1BeY7bD12z_JWMvUnTfPceSr2DBynrwPxNXg3qjN1JWrbZznsQFh2QBvCgR7dwOzCiEePlAx_rZP2-5yc9g4xC20FcFasNc_uShyssyVYDufz-ub6k3awC4vr0wuL8OfWl0gmGYFSLUkzXxh2LW1xot_DZhOqDCbzItdBpVC3lSJjLCIKOQSuSGrPQZYuXTt1JWtNiYlXKDrVnjQNGOJJUd5TDtUWhqKIZRJtIcZkpe9xweJ1W5aLF7_pc0Rt_0EFiDNbqZH1s6psunv-a0doW6V9omUVu-o0aKDCo-dDlm4JmrsZE_qhiikXgMnZJKimIz4Ffp-WHBISiH3BpYmjG3n_4KBzG6_dC4KugUXbSmmIOorzXus5n4mYy_iiaddbt5hGmM6NVj5kBnk_CwUdSlOGf2-jNJoVSJKtPx8O2gzSVrZsVV0dB3PgyzH_I5yttUla0Mu9tqc3kRcZaw4KHHuVYULPWLgsUcBxKMHfICzeypG7tUNdizoVVb2CJ5wxfMw-uy-SG3i29wSlsdnikGxNnrRIHv16FSOJb6-Hm8q_o-PMIpGZYh2gd0HxLO7MdZb9mB26lIt4zEcipimTicc0OMPQw7gkRqTz34nH_6CjGoLVAjd0f3mh2lCr1if8A0g0xDROL5JI3IUAFNADvJzpPEZVTpU0lnqpTnw09e7-IUImHGhMoBsg1WqZcC0ORW7s18s22EYoDJZVMDDWNB3DrhI6HQ-WiJJavQ39Hgi36IojALnFp408odcGTBLwwS-Uq-uEw7xmLOwbEMBjBpIN86z3cCXygvggS1h9u_phCu9WnKiTdeg1aRqwn_JL87kRvkA2zHBL4w3MOd2024HOu9pU31u4lamLxNZ8YwhI2Digmi0MkfgpoWCMjB0T6rz3gobtt7kETxzxx71UHDj3b5jM1e5bqq9kuV6pW5yB8vYU-z0YIUHynSKKRnKrRFunzhGO1jy1X0yaA5SgPs05lboddcFXarbhcjvc2WrL9tVrh8qY5lPIylutK3DbR35F0ToMJIGKWR_7iCcTwTJ-0QZSxuStrX7mVBUfBy6OI5knWG-_qFZ8V6Ov75vIrHm8Vh10kVcdLDM4y8iNnjunNOy_zpIsoJGNJqyjoswI08Bj9YiPeKQbSm0H-DeAZdmbXy5mQwWRTNvv3akhsOdnDU-v-rYSyhKrjn6pckot3Z4cqlrL19tK7Mbl9wp5EY0Yd66qhGpT2KTnyl08R1dGGm4XbRKrUZDD-xhOBASELF1s76SMgvxREAVbF_PP-68-3u5JWhSShOCzKsLr_x9Eggn-nNA-5hGHUrJ31T6fvTwdtta3_wj6Zai40swWR6__vW_FMFejpyn_OmXDrbcFXdmH5dSAYxHQa4WVcCtkz6jMR-xg3Bnam72-gSgO0yH8Nr1-aCqOYK1JPzNSj0UaPTnf2-aoZtruvP1ClUi9DNvyY7IQdWCUsLw2GXV_AU=\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Simulating PDF generation**\\n\\nI’m thinking about simulating specific PDF entry generation without having to rebuild everything. I wonder if I can use existing functions by importing them. It looks like I might not need a big edit. I could test generating a printable HTML/PDF for the Tailscale entry from that day using the Python import build module. I'll check the output text length or page count, and maybe generate a temporary PDF for inspection. Cleanup for the temp files will also be necessary. Let’s keep it simple!\"}]}"},{"type":"toolCall","id":"call_rbpE7nOsNw4OxGp24f8gATSx|fc_0f029265420fe376016a1c040095388191b67859606652cc4c","name":"todo","arguments":{"action":"create","subject":"Validate Tailscale-only PDF rebuild reduction","description":"Regenerate a Tailscale changelog printable/PDF for the affected entry using current builder logic, compare whether content is now narrowly scoped, and clean temp artifacts.","activeForm":"validating tailscale pdf scope","status":"in_progress","owner":"agent"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":613,"output":181,"cacheRead":104960,"cacheWrite":0,"totalTokens":105754,"cost":{"input":0.0015325000000000002,"output":0.002715,"cacheRead":0.02624,"cacheWrite":0,"total":0.0304875}},"stopReason":"toolUse","timestamp":1780220923080,"responseId":"resp_0f029265420fe376016a1c03fb27d48191a09f0a96c42d8f75"}}
{"type":"message","id":"bff44684","parentId":"147751b7","timestamp":"2026-05-31T09:48:48.888Z","message":{"role":"toolResult","toolCallId":"call_rbpE7nOsNw4OxGp24f8gATSx|fc_0f029265420fe376016a1c040095388191b67859606652cc4c","toolName":"todo","content":[{"type":"text","text":"Created #6: Validate Tailscale-only PDF rebuild reduction (pending)"}],"details":{"action":"create","params":{"action":"create","subject":"Validate Tailscale-only PDF rebuild reduction","description":"Regenerate a Tailscale changelog printable/PDF for the affected entry using current builder logic, compare whether content is now narrowly scoped, and clean temp artifacts.","activeForm":"validating tailscale pdf scope","status":"in_progress","owner":"agent"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"completed","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"},{"id":3,"subject":"Tighten rss-offline PDF extraction for noisy blogs","status":"completed","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","owner":"agent"},{"id":4,"subject":"Audit all recent rss-offline feed domains for PDF targeting","status":"completed","description":"Inspect current entries by domain, identify article-body selectors per host where useful, extend builder host-specific print root targeting, and clean any temporary artifacts. No rebuild unless needed for validation-only local parsing.","activeForm":"auditing rss-offline domains","owner":"agent"},{"id":5,"subject":"Rebuild rss-offline latest PDF after targeting improvements","status":"completed","description":"Run rss-offline builder so the newest PDF is regenerated with the new host-specific targeting, then report the resulting latest PDF size.","activeForm":"rebuilding rss-offline pdf","owner":"agent"},{"id":6,"subject":"Validate Tailscale-only PDF rebuild reduction","status":"pending","description":"Regenerate a Tailscale changelog printable/PDF for the affected entry using current builder logic, compare whether content is now narrowly scoped, and clean temp artifacts.","activeForm":"validating tailscale pdf scope","owner":"agent"}],"nextId":7},"isError":false,"timestamp":1780220928888}}
{"type":"message","id":"ce8fc586","parentId":"bff44684","timestamp":"2026-05-31T09:49:00.710Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_xAN06gAh7d0msR1eX07KvP64|fc_0f029265420fe376016a1c0403ace4819199bf204914386c1f","name":"bash","arguments":{"command":"python - <<'PY'\nimport json, importlib.util, tempfile, subprocess, os\nfrom pathlib import Path\nfrom bs4 import BeautifulSoup\n\nspec=importlib.util.spec_from_file_location('rssbuild','/home/sebas/work/projects/rss-offline/build.py')\nmod=importlib.util.module_from_spec(spec)\nspec.loader.exec_module(mod)\n\nentries=json.loads(Path('/var/www/rss-offline/entries.json').read_text())\nentry=next(e for e in entries if e['link']=='https://tailscale.com/changelog/#2026-05-29-service')\nprint('entry', entry['link'])\nprint('local', entry['local_path'])\n\nsrc=Path('/var/www/rss-offline') / Path(entry['local_path']).name\norig_html=src.read_text()\norig_soup=BeautifulSoup(orig_html,'html.parser')\norig_text=len(orig_soup.get_text(' ',strip=True))\niso=mod.isolate_anchor_section(BeautifulSoup(orig_html,'html.parser'), entry)\niso_text=len(iso.get_text(' ',strip=True))\npicked=mod.pick_print_root(BeautifulSoup(str(iso),'html.parser'), entry)\npicked_text=len(picked.get_text(' ',strip=True))\nprint('text_lengths', {'orig':orig_text,'isolated':iso_text,'picked':picked_text})\n\nprintable=mod.make_pdf_printable_copy(entry)\nprintable_text=len(BeautifulSoup(printable.read_text(),'html.parser').get_text(' ',strip=True))\nprint('printable', printable)\nprint('printable_text', printable_text)\n\nwith tempfile.NamedTemporaryFile(prefix='tailscale-test-', suffix='.pdf', delete=False, dir='/tmp') as tf:\n    pdf_path=Path(tf.name)\ntry:\n    mod.print_pdf(printable, pdf_path)\n    info=subprocess.run(['pdfinfo', str(pdf_path)], capture_output=True, text=True, check=True).stdout\n    for line in info.splitlines():\n        if line.startswith(('Pages:','File size:','Title:')):\n            print(line)\nfinally:\n    try: os.unlink(pdf_path)\n    except FileNotFoundError: pass\nPY","timeout":600}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":307,"output":501,"cacheRead":105472,"cacheWrite":0,"totalTokens":106280,"cost":{"input":0.0007675000000000001,"output":0.007515,"cacheRead":0.026368,"cacheWrite":0,"total":0.0346505}},"stopReason":"toolUse","timestamp":1780220928891,"responseId":"resp_0f029265420fe376016a1c0400f46081919ee5ca7924dc4dd1"}}
{"type":"message","id":"f0db7ba7","parentId":"ce8fc586","timestamp":"2026-05-31T09:49:01.750Z","message":{"role":"toolResult","toolCallId":"call_xAN06gAh7d0msR1eX07KvP64|fc_0f029265420fe376016a1c0403ace4819199bf204914386c1f","toolName":"bash","content":[{"type":"text","text":"entry https://tailscale.com/changelog/#2026-05-29-service\nlocal /rss-offline/article-04009026a7f5cca2.html\ntext_lengths {'orig': 112122, 'isolated': 339, 'picked': 339}\nTraceback (most recent call last):\n  File \"<stdin>\", line 24, in <module>\n  File \"/home/sebas/work/projects/rss-offline/build.py\", line 407, in make_pdf_printable_copy\n    printable_path.write_text(html)\n    ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^\n  File \"/usr/lib/python3.13/pathlib/_local.py\", line 557, in write_text\n    return PathBase.write_text(self, data, encoding, errors, newline)\n           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.13/pathlib/_abc.py\", line 651, in write_text\n    with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:\n         ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.13/pathlib/_local.py\", line 539, in open\n    return io.open(self, mode, buffering, encoding, errors, newline)\n           ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nPermissionError: [Errno 13] Permission denied: '/var/www/rss-offline/pdf/article-04009026a7f5cca2.print.html'\n\n\nCommand exited with code 1"}],"details":{},"isError":true,"timestamp":1780220941750}}
{"type":"message","id":"48a25a94","parentId":"f0db7ba7","timestamp":"2026-05-31T09:49:11.962Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"","thinkingSignature":"{\"id\":\"rs_0f029265420fe376016a1c040ecf4c81918c5f9dd1f368bbfa\",\"type\":\"reasoning\",\"encrypted_content\":\"gAAAAABqHAQS6FdbikSCmoWcutUdZ2gq8TpUCfgUSW8mS53s2wcgqTd8GZFMJ_TMJKQ6JEpL3gWSOPA4FUp4ocb3DO3BYRwXkKUs2zbwbhiB7JuwAYRb4F22O--Eg0avRqHCCREw5jezyBvK_tWU3WsQ23R2dh347fLrNjV2hg_258ArwRJ3hveeULbw_VE_Bid8tOeBS0-ltUhxjPjL4eVZBwmgWOnIazM2V1SN2fxay6F6sRZ5LO7ZOJPJlSJoqnPMCLUuT2qMR8-jpy9GXcdF05MzmTYbGuG-4mA0b9Jhp7qABDAAHPrjtdo0-Fq7HLjF6jxyxzLuiyKtrt2E_8XJYScvy0085UPUNotwiED464dXfJXctPuHeJa7rlebUxz4eOo7kkidAuaIWS9stHJGpTe-9eEV2A--KjnVfmvWNFJ50bNH9XkCMdyGRmSlEFPYqh6DrHhUtOl-A7JaJLCEG9zJ8kjQLNEW-C0wXvCXr9yAeHsvDS4G5oGrYVf2LM_mmTmTR22u0vCVz_4aCtFRAjyecJVT6HwNymRzbDw8j5eMTQSXMnsWdqDLr_W7YVips5B_WPVcIemZSm5lcz0wu6JX5fLk8-4W-UUcrM0RuPOHyUjk-ATpPL9ZnFMmoyfxcBG_dsBmGfmNVssU6ZO76zbhikMa7EP1rG4nMkDfXYUTR0Mr2qTRVo6E085NxRY0ig-ibibTp0wscNXF6qH-BBN_gTuh_2JXc_fLm8UC9c0c8PIrK1JHzxHDfB-X8pkHLemKV0QwSlfFKesPjAPJtuiPGDz4VP9BzFi4aO8wDmYDtr5pv7OzYVKa7TQptbftn-h3RT-CqzSzmUlZuwgiuKSo3-KQRIIPFXdDAmZ7ulQ8FPCC8CbVIGL1YcefEzRRlx8hP-XIoHhw7jc56_3kd_40cD5HlVGJzLWNkEPn_DeqAJbnZOXxzJ7K2dgS5-kWyrx9YhTHvVFtakVtTXN_jx5QI6D84w4pkMysM5NLC_Zl_fcq6VsVdYlm6f0Zto_i2D7AGCb1V3EY0izcNFEUZuuii8wCtcLRWWMmJhS3Ol-mwf1Q89BiYmr7FSX_x9rvwgj69tLwVyCFIvI_pIZ1rzyG-R9j5g0Nvah06kdAzyW4iu_GyP-fDsyPE5haR0Mdvjt5uxDQlKIH-1vkSZK1U98v9pCZCZprrg03F4vnMZN0_yqz3_BVjy5b0QVUU1PG-_MjOSPe8F5xLgIDxE6kowdgW3bUJw==\",\"summary\":[]}"},{"type":"toolCall","id":"call_WIkrvbhEOp7nCGp0YzfSl188|fc_0f029265420fe376016a1c04123f7c81919269a29d38b3919d","name":"bash","arguments":{"command":"python - <<'PY'\nimport json, importlib.util, tempfile, subprocess, shutil\nfrom pathlib import Path\nfrom bs4 import BeautifulSoup\n\nspec=importlib.util.spec_from_file_location('rssbuild','/home/sebas/work/projects/rss-offline/build.py')\nmod=importlib.util.module_from_spec(spec)\nspec.loader.exec_module(mod)\n\nentries=json.loads(Path('/var/www/rss-offline/entries.json').read_text())\nentry=next(e for e in entries if e['link']=='https://tailscale.com/changelog/#2026-05-29-service')\norig_html=(Path('/var/www/rss-offline')/Path(entry['local_path']).name).read_text()\nprint('text_lengths', {\n  'orig': len(BeautifulSoup(orig_html,'html.parser').get_text(' ',strip=True)),\n  'isolated': len(mod.isolate_anchor_section(BeautifulSoup(orig_html,'html.parser'), entry).get_text(' ',strip=True)),\n})\n\ntmpdir=Path(tempfile.mkdtemp(prefix='rss-offline-tailtest-', dir='/tmp'))\ntry:\n    mod.OUTPUT_DIR = tmpdir\n    shutil.copy2(Path('/var/www/rss-offline')/Path(entry['local_path']).name, tmpdir/Path(entry['local_path']).name)\n    printable=mod.make_pdf_printable_copy(entry)\n    txt=len(BeautifulSoup(printable.read_text(),'html.parser').get_text(' ',strip=True))\n    print('printable_text', txt)\n    pdf_path=tmpdir/'tailscale-test.pdf'\n    mod.print_pdf(printable, pdf_path)\n    info=subprocess.run(['pdfinfo', str(pdf_path)], capture_output=True, text=True, check=True).stdout\n    for line in info.splitlines():\n        if line.startswith(('Pages:','File size:')):\n            print(line)\nfinally:\n    shutil.rmtree(tmpdir, ignore_errors=True)\nPY","timeout":600}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":1135,"output":498,"cacheRead":105472,"cacheWrite":0,"totalTokens":107105,"cost":{"input":0.0028375,"output":0.00747,"cacheRead":0.026368,"cacheWrite":0,"total":0.0366755}},"stopReason":"toolUse","timestamp":1780220941752,"responseId":"resp_0f029265420fe376016a1c040dd07881918261ca0e30a2f657"}}
{"type":"message","id":"60ecbfb0","parentId":"48a25a94","timestamp":"2026-05-31T09:49:13.840Z","message":{"role":"toolResult","toolCallId":"call_WIkrvbhEOp7nCGp0YzfSl188|fc_0f029265420fe376016a1c04123f7c81919269a29d38b3919d","toolName":"bash","content":[{"type":"text","text":"text_lengths {'orig': 112122, 'isolated': 339}\nprintable_text 339\nPages:           1\nFile size:       34283 bytes\n"}],"isError":false,"timestamp":1780220953840}}
{"type":"message","id":"e2c7b791","parentId":"60ecbfb0","timestamp":"2026-05-31T09:49:15.444Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_0YqykgnSvLh5YVrFeKXAy90K|fc_0f029265420fe376016a1c041af9cc8191a401658cbe094335","name":"todo","arguments":{"action":"update","id":6,"status":"completed"}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":657,"output":25,"cacheRead":106496,"cacheWrite":0,"totalTokens":107178,"cost":{"input":0.0016425,"output":0.000375,"cacheRead":0.026624,"cacheWrite":0,"total":0.0286415}},"stopReason":"toolUse","timestamp":1780220953842,"responseId":"resp_0f029265420fe376016a1c0419e80481918cf4b42f20951dd2"}}
{"type":"message","id":"939e9285","parentId":"e2c7b791","timestamp":"2026-05-31T09:49:15.444Z","message":{"role":"toolResult","toolCallId":"call_0YqykgnSvLh5YVrFeKXAy90K|fc_0f029265420fe376016a1c041af9cc8191a401658cbe094335","toolName":"todo","content":[{"type":"text","text":"Updated #6 (pending → completed)"}],"details":{"action":"update","params":{"action":"update","id":6,"status":"completed"},"tasks":[{"id":1,"subject":"Enable native visited styling for PDF links in rss-offline","status":"completed","description":"Inspect rss-offline app/site and adjust PDF links so browser native :visited state marks them as read when opened from https://ballbox-first.emperor-ratio.ts.net/rss-offline/.","activeForm":"checking rss-offline links"},{"id":2,"subject":"Add direct PDF download button and preserve rss-offline scroll position","status":"completed","description":"Update rss-offline PDF archive so each item has a separate direct download button while main link remains readable/visited, and restore page scroll position after opening PDFs.","activeForm":"updating rss-offline PDF UX","owner":"agent"},{"id":3,"subject":"Tighten rss-offline PDF extraction for noisy blogs","status":"completed","description":"Inspect recent PDF inputs, especially GitHub Blog and DigitalOcean, and improve builder targeting/cleanup so printable copies focus on article content with less extra chrome. Do not rebuild.","activeForm":"tightening rss-offline PDF extraction","owner":"agent"},{"id":4,"subject":"Audit all recent rss-offline feed domains for PDF targeting","status":"completed","description":"Inspect current entries by domain, identify article-body selectors per host where useful, extend builder host-specific print root targeting, and clean any temporary artifacts. No rebuild unless needed for validation-only local parsing.","activeForm":"auditing rss-offline domains","owner":"agent"},{"id":5,"subject":"Rebuild rss-offline latest PDF after targeting improvements","status":"completed","description":"Run rss-offline builder so the newest PDF is regenerated with the new host-specific targeting, then report the resulting latest PDF size.","activeForm":"rebuilding rss-offline pdf","owner":"agent"},{"id":6,"subject":"Validate Tailscale-only PDF rebuild reduction","status":"completed","description":"Regenerate a Tailscale changelog printable/PDF for the affected entry using current builder logic, compare whether content is now narrowly scoped, and clean temp artifacts.","activeForm":"validating tailscale pdf scope","owner":"agent"}],"nextId":7},"isError":false,"timestamp":1780220955444}}
{"type":"message","id":"49ca41b5","parentId":"939e9285","timestamp":"2026-05-31T09:49:19.309Z","message":{"role":"assistant","content":[{"type":"text","text":"Sí. Lo probé simple y da bien.\n\nTest:\n- entry:\n  - `https://tailscale.com/changelog/#2026-05-29-service`\n\nResultado con lógica nueva:\n- HTML original capturado:\n  - `112122` chars de texto\n- después de aislar el anchor:\n  - `339`\n- printable final:\n  - `339`\n- PDF generado en test:\n  - `1 page`\n  - `34283 bytes`\n\nO sea:\n- antes: agarraba la página enorme/histórica\n- ahora: queda recortado a la entrada puntual\n\nY limpié lo temporal en `/tmp`.","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1c041cbf6c81919a57de03a8666d5b\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":188,"output":136,"cacheRead":107008,"cacheWrite":0,"totalTokens":107332,"cost":{"input":0.00047000000000000004,"output":0.00204,"cacheRead":0.026751999999999998,"cacheWrite":0,"total":0.029261999999999996}},"stopReason":"stop","timestamp":1780220955446,"responseId":"resp_0f029265420fe376016a1c041b86848191ad2c2909cd8eaeef"}}
{"type":"message","id":"d2a00894","parentId":"49ca41b5","timestamp":"2026-05-31T16:21:17.633Z","message":{"role":"user","content":[{"type":"text","text":"/new"}],"timestamp":1780244477631}}
{"type":"message","id":"6733b99a","parentId":"d2a00894","timestamp":"2026-05-31T16:21:21.274Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_HUPg5ypsZp0EyDCGrWol4iFW|fc_0f029265420fe376016a1c6000a72c8191805d68ef63ccc1f7","name":"bash","arguments":{"command":"/home/sebas/pi-config/bin/pi-telegram-new-session","timeout":120}}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":107179,"output":33,"cacheRead":0,"cacheWrite":0,"totalTokens":107212,"cost":{"input":0.2679475,"output":0.000495,"cacheRead":0,"cacheWrite":0,"total":0.26844250000000003}},"stopReason":"toolUse","timestamp":1780244477638,"responseId":"resp_0f029265420fe376016a1c5ffea9fc819181bb319a6f0458b4"}}
{"type":"message","id":"6ab73d56","parentId":"6733b99a","timestamp":"2026-05-31T16:21:21.292Z","message":{"role":"toolResult","toolCallId":"call_HUPg5ypsZp0EyDCGrWol4iFW|fc_0f029265420fe376016a1c6000a72c8191805d68ef63ccc1f7","toolName":"bash","content":[{"type":"text","text":"queued new Telegram Pi session: pi-telegram\n"}],"isError":false,"timestamp":1780244481292}}
{"type":"message","id":"15e4e466","parentId":"6ab73d56","timestamp":"2026-05-31T16:21:22.810Z","message":{"role":"assistant","content":[{"type":"text","text":"Listo. Roté la sesión de Telegram Pi.","textSignature":"{\"v\":1,\"id\":\"msg_0f029265420fe376016a1c60026d3881918c53dff70321f739\",\"phase\":\"final_answer\"}"}],"api":"openai-codex-responses","provider":"openai-codex","model":"gpt-5.4","usage":{"input":225,"output":15,"cacheRead":107008,"cacheWrite":0,"totalTokens":107248,"cost":{"input":0.0005625000000000001,"output":0.000225,"cacheRead":0.026751999999999998,"cacheWrite":0,"total":0.027539499999999998}},"stopReason":"stop","timestamp":1780244481295,"responseId":"resp_0f029265420fe376016a1c60015ea0819186dc84b1cc82b896"}}
