<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Omachoko Tanimu Yakubu</title>
    <description>The latest articles on DEV Community by Omachoko Tanimu Yakubu (@omachokoyakubu).</description>
    <link>https://dev.to/omachokoyakubu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2798866%2Fdc1514fc-616f-40cb-9972-72950a2bd0f7.png</url>
      <title>DEV Community: Omachoko Tanimu Yakubu</title>
      <link>https://dev.to/omachokoyakubu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/omachokoyakubu"/>
    <language>en</language>
    <item>
      <title>Hacking the Brain: How I Built a Custom Proxy to Run Claude Code on Gemini 2.0 Flash (For Free)</title>
      <dc:creator>Omachoko Tanimu Yakubu</dc:creator>
      <pubDate>Mon, 11 May 2026 15:37:28 +0000</pubDate>
      <link>https://dev.to/omachokoyakubu/hacking-the-brain-how-i-built-a-custom-proxy-to-run-claude-code-on-gemini-20-flash-for-free-3bkh</link>
      <guid>https://dev.to/omachokoyakubu/hacking-the-brain-how-i-built-a-custom-proxy-to-run-claude-code-on-gemini-20-flash-for-free-3bkh</guid>
      <description>&lt;h2&gt;
  
  
  The Hook: The $500 Question
&lt;/h2&gt;

&lt;p&gt;We’ve all seen the demo: &lt;strong&gt;Claude Code&lt;/strong&gt; is a beast. It’s an agentic CLI that doesn’t just suggest code; it &lt;em&gt;lives&lt;/em&gt; in your terminal, runs bash commands, searches your filesystem, and executes complex multi-step audits. &lt;/p&gt;

&lt;p&gt;But for those of us in the Web3 and Cybersecurity space, the native Anthropic API comes with three massive friction points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Aggressive Rate Limits&lt;/strong&gt; on the Opus/Sonnet tiers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Token Costs&lt;/strong&gt; when analyzing 100k+ line smart contract repos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regional Geofencing&lt;/strong&gt; that can kill your workflow mid-audit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wanted the &lt;strong&gt;interface&lt;/strong&gt; of Claude Code, the &lt;strong&gt;brain&lt;/strong&gt; of Gemini 2.0 Flash (with its massive 1M context window), and the &lt;strong&gt;pricing&lt;/strong&gt; of OpenRouter (currently $0.00 for Gemini 2.0 Flash). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the story of how I hacked the protocol to make it happen.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conflict: Why Standard Routers Fail
&lt;/h2&gt;

&lt;p&gt;I started where most do: the community-built &lt;code&gt;@musistudio/claude-code-router&lt;/code&gt; (&lt;code&gt;ccr&lt;/code&gt;). It’s a great project, but in the trenches of a Linux environment, it hit a wall. &lt;/p&gt;

&lt;p&gt;The "Provider Not Found" errors and the lack of deep-packet inspection meant that Claude Code’s hidden "power features"—like &lt;strong&gt;Prompt Caching&lt;/strong&gt; and &lt;strong&gt;Strict Tool Selection&lt;/strong&gt;—were actually breaking the connection to OpenRouter.&lt;/p&gt;

&lt;p&gt;Specifically, we were getting slammed with &lt;strong&gt;HTTP 400 Errors&lt;/strong&gt; from Amazon Bedrock. Why? Because Claude Code was sending &lt;code&gt;cache_control&lt;/code&gt; tags that Bedrock didn't understand. The standard router was just a postman; it wasn't a translator.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deep Dive: The Three Pillars of the "God-Mode" Proxy
&lt;/h2&gt;

&lt;p&gt;To solve this, I moved away from pre-built tools and engineered a custom &lt;strong&gt;Node.js Universal Translator Proxy&lt;/strong&gt;. Here is the technical architecture that solved the puzzle:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The "Deep-Clean" Logic
&lt;/h3&gt;

&lt;p&gt;Claude Code is opinionated. It injects &lt;code&gt;cache_control: {"type": "ephemeral"}&lt;/code&gt; into its payloads. Most OpenRouter providers see this and panic. My proxy implements a recursive cleaner that scrubs these tags before the data ever leaves your machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Protocol Translation (Anthropic ↔ OpenAI)
&lt;/h3&gt;

&lt;p&gt;This was the hardest part. Anthropic uses a specific Server-Sent Events (SSE) format for streaming. OpenAI/OpenRouter uses another. If you get this wrong, you get the dreaded &lt;strong&gt;"Empty or Malformed Response (HTTP 200)"&lt;/strong&gt;. My proxy translates the "Typewriter" effect in real-time, making Gemini feel like a native Claude model.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Agentic Continuity
&lt;/h3&gt;

&lt;p&gt;Claude Code isn't just one message; it's a conversation. When the AI runs a tool (like &lt;code&gt;ls&lt;/code&gt; or &lt;code&gt;grep&lt;/code&gt;), it needs to see that tool's result in its history. My proxy maps these "Tool Results" back into the OpenAI &lt;code&gt;tool&lt;/code&gt; role, ensuring the AI never loses its train of thought.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Blueprint: How to Replicate This Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 0: Prerequisites &amp;amp; Installation
&lt;/h3&gt;

&lt;p&gt;Before we touch the proxy, ensure you have the core tools installed. Open your terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Install Node.js (if not already present)&lt;/span&gt;
&lt;span class="c"&gt;# On Debian/Ubuntu/Parrot:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs npm

&lt;span class="c"&gt;# 2. Install Claude Code globally&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @anthropic-ai/claude-code

&lt;span class="c"&gt;# 3. Create your proxy directory&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/claude-code-proxy &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/claude-code-proxy

&lt;span class="k"&gt;**&lt;/span&gt;Note&lt;span class="k"&gt;**&lt;/span&gt;: You &lt;span class="k"&gt;do &lt;/span&gt;NOT need to &lt;span class="s2"&gt;"install"&lt;/span&gt; OpenRouter. It is a cloud API that we communicate with via HTTP requests. As long as you have Node.js, you&lt;span class="s1"&gt;'re ready.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: The Environment
&lt;/h3&gt;

&lt;p&gt;Forget the complex JSON configs. All you need is three lines in your &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Point Claude to your local bridge&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:3456/v1
&lt;span class="c"&gt;# Use your OpenRouter Key&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-or-v1-xxxx...
&lt;span class="c"&gt;# Tell Claude to use a "Sonnet" name (the proxy will swap the brain)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLAUDE_CODE_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;claude-3-5-sonnet-20240620
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: The Proxy (The Secret Sauce)
&lt;/h3&gt;

&lt;p&gt;This is the final, battle-tested script. Save it as &lt;code&gt;~/proxy.js&lt;/code&gt; to enable full agentic capabilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_OPENROUTER_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TARGET_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;google/gemini-2.0-flash-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="c1"&gt;// History Translation&lt;/span&gt;
    &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toolCalls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool_use&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toolResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool_result&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolCalls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tool_calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toolCalls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
              &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}));&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;toolResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tool_call_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tool_use_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;toolResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://openrouter.ai/api/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HTTP-Referer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://github.com/anthropic-ai/claude-code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TARGET_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input_schema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;anthropic-version&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-06-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextDecoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: message_start\ndata: {"type":"message_start","message":{"id":"msg_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;","type":"message","role":"assistant","content":[],"model":"claude-3-5-sonnet-20240620","stop_reason":null,"usage":{"input_tokens":0,"output_tokens":0}}}\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: content_block_start\ndata: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;toolCallsDelta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[DONE]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: content_block_delta\ndata: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;}}\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;toolCallsDelta&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;toolCallsDelta&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
              &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;toolCallsDelta&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;toolCallsDelta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toolCallsDelta&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: content_block_start\ndata: {"type":"content_block_start","index":&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,"content_block":{"type":"tool_use","id":"&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;","name":"&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;","input":&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;}}\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: message_stop\ndata: {"type":"message_stop"}\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3456&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Launching the Environment
&lt;/h3&gt;

&lt;p&gt;To get your new agentic setup running, you will need two terminal sessions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terminal 1: The Proxy Bridge&lt;/strong&gt;&lt;br&gt;
First, clear any old processes and start your translator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Kill any existing process on our port&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;fuser &lt;span class="nt"&gt;-k&lt;/span&gt; 3456/tcp
&lt;span class="c"&gt;# Launch the proxy in the background&lt;/span&gt;
node ~/proxy.js &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terminal 2: The Agent&lt;/strong&gt;&lt;br&gt;
Now, simply launch Claude Code as you normally would. It will automatically route through your bridge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Verdict: Total Agentic Power
&lt;/h2&gt;

&lt;p&gt;Once this bridge was built, the results were staggering. I asked Claude to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;List all &lt;code&gt;.txt&lt;/code&gt; files in my home directory.&lt;/strong&gt; (It succeeded using &lt;code&gt;ls&lt;/code&gt;.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Study a complex Web3 workspace (&lt;code&gt;glider-poc&lt;/code&gt;).&lt;/strong&gt; (It correctly identified the Foundry setup, Solidity audits, and Polygon AggLayer integration.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spawn sub-agents.&lt;/strong&gt; (The sub-agents worked perfectly, summarizing massive markdown files.)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;For Python Developers and AI Engineers, this represents the &lt;strong&gt;democratization of agentic tools&lt;/strong&gt;. You are no longer tethered to a single provider's credits or their regional whims. You have built a universal adapter.&lt;/p&gt;

&lt;p&gt;In Cybersecurity, we often say that "the best defense is a deep understanding of the protocols." Today, we didn't just use a tool—we understood the API protocol well enough to rewrite it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are you ready to hack your workflow?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Find the repository for easy setup and future updates on my GitHub: &lt;a href="https://github.com/OmachokoYakubu/claude-code-proxy" rel="noopener noreferrer"&gt;https://github.com/OmachokoYakubu/claude-code-proxy&lt;/a&gt;, and let's build the future of AI-assisted security together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Omachoko Yakubu&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;CYBERSECURITY ANALYST (WEB2/WEB3) | PYTHON DEVELOPER &amp;amp; AI/LLM ENGINEER&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>gemini</category>
      <category>showdev</category>
    </item>
    <item>
      <title>HNG12 STAGE 1: NUMBER CLASSIFICATION API DEVELOPMENT &amp; DEPLOYMENT</title>
      <dc:creator>Omachoko Tanimu Yakubu</dc:creator>
      <pubDate>Wed, 05 Feb 2025 16:34:16 +0000</pubDate>
      <link>https://dev.to/omachokoyakubu/hng12-stage-1-number-classification-api-development-deployment-1if1</link>
      <guid>https://dev.to/omachokoyakubu/hng12-stage-1-number-classification-api-development-deployment-1if1</guid>
      <description>&lt;p&gt;&lt;strong&gt;INTRODUCTION&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The task required the development of an API that classifies numbers based on mathematical properties and provides a fun fact retrieved from an external API. The API must return data in JSON format, be deployed publicly, and adhere to quality standards. The acceptance criteria ensure the API is functional, well-documented, and efficiently deployed.&lt;/p&gt;

&lt;p&gt;I then proceeded to update the package list and installed necessary system tools to ensure a smooth development environment. I ran the following commands:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade -y&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;sudo apt install python3 python3-pip git curl -y&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvguphdoy15dymhbuhp64.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvguphdoy15dymhbuhp64.PNG" alt=" " width="664" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This ensured that Python, Pip, Git, and Curl were available for use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SETTING UP THE PROJECT DIRECTORY&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I created a dedicated directory for the project and navigated into it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mkdir number_api &amp;amp;&amp;amp; cd number_api&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32ql2kk0vzqe9zg5wbqm.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32ql2kk0vzqe9zg5wbqm.PNG" alt=" " width="576" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having created the directory I installed FastAPI and Uvicorn, which will serve as the API framework and ASGI server respectively:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pip install fastapi uvicorn requests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpsl1jv42jtdjqzprecw.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpsl1jv42jtdjqzprecw.PNG" alt=" " width="723" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This step ensures that the necessary libraries are available for developing and running the API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WRITING THE API CODE&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using a text editor, I created and edited main.py:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;nano main.py&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I followed that up with the following code, which defines the API functionality: &lt;/p&gt;

&lt;p&gt;`from fastapi import FastAPI, Query, HTTPException, Request&lt;br&gt;
from fastapi.responses import JSONResponse&lt;br&gt;
import requests&lt;br&gt;
from typing import List&lt;br&gt;
from fastapi.middleware.cors import CORSMiddleware&lt;br&gt;
app = FastAPI()&lt;/p&gt;

&lt;h1&gt;
  
  
  CORS configuration
&lt;/h1&gt;

&lt;p&gt;origins = [&lt;br&gt;
    "&lt;a href="http://localhost" rel="noopener noreferrer"&gt;http://localhost&lt;/a&gt;",&lt;br&gt;
    "&lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;",&lt;br&gt;
    "&lt;em&gt;",  # Allows requests from any origin (USE WITH CAUTION IN PRODUCTION)&lt;br&gt;
]&lt;br&gt;
app.add_middleware(&lt;br&gt;
    CORSMiddleware,&lt;br&gt;
    allow_origins=origins,&lt;br&gt;
    allow_credentials=True,&lt;br&gt;
    allow_methods=["&lt;/em&gt;"],&lt;br&gt;
    allow_headers=["&lt;em&gt;"],&lt;br&gt;
)&lt;br&gt;
def is_prime(n: int) -&amp;gt; bool:&lt;br&gt;
    """Check if a number is prime."""&lt;br&gt;
    if n &amp;lt; 2:&lt;br&gt;
        return False&lt;br&gt;
    for i in range(2, int(abs(n) *&lt;/em&gt; 0.5) + 1):&lt;br&gt;
        if n % i == 0:&lt;br&gt;
            return False&lt;br&gt;
    return True&lt;br&gt;
def is_armstrong(n: int) -&amp;gt; bool:&lt;br&gt;
    """Check if a number is an Armstrong number."""&lt;br&gt;
    num_str = str(abs(n))&lt;br&gt;
    digits = [int(d) for d in num_str]&lt;br&gt;
    power = len(digits)&lt;br&gt;
    return sum(d ** power for d in digits) == abs(n)&lt;br&gt;
def digit_sum(n: int) -&amp;gt; int:&lt;br&gt;
    """Calculate the sum of the digits of a number."""&lt;br&gt;
    return sum(int(d) for d in str(abs(n)))&lt;br&gt;
def get_fun_fact(n: int) -&amp;gt; str:&lt;br&gt;
    """Fetch a fun fact about a number from the Numbers API."""&lt;br&gt;
    try:&lt;br&gt;
        response = requests.get(f"&lt;a href="http://numbersapi.com/%7Bn%7D/math%22" rel="noopener noreferrer"&gt;http://numbersapi.com/{n}/math"&lt;/a&gt;)&lt;br&gt;
        response.raise_for_status()&lt;br&gt;
        return response.text&lt;br&gt;
    except requests.exceptions.RequestException:&lt;br&gt;
        return "No fun fact available."&lt;br&gt;
@app.exception_handler(ValueError)&lt;br&gt;
async def value_error_handler(request: Request, exc: ValueError):&lt;br&gt;
    return JSONResponse(&lt;br&gt;
        status_code=400,&lt;br&gt;
        content={"number": request.query_params.get("number", ""), "error": True},&lt;br&gt;
    )&lt;br&gt;
@app.get("/api/classify-number")&lt;br&gt;
def classify_number(number: str = Query(..., description="Enter a number")):&lt;br&gt;
    """&lt;br&gt;
    API endpoint to classify a number.&lt;br&gt;
    Accepts numbers in different formats (negative, string, float, etc.).&lt;br&gt;
    """&lt;br&gt;
    original_input = number  # Store the exact user input&lt;br&gt;
    try:&lt;br&gt;
        # Convert input to an integer, even if it's a float or string&lt;br&gt;
        parsed_number = int(float(number))&lt;br&gt;&lt;br&gt;
    except ValueError:&lt;br&gt;
        raise ValueError("Invalid input")&lt;br&gt;
    is_negative = parsed_number &amp;lt; 0&lt;br&gt;
    abs_number = abs(parsed_number)&lt;br&gt;
    properties: List[str] = []&lt;br&gt;
    is_armstrong_num = is_armstrong(abs_number)&lt;br&gt;
    is_odd = abs_number % 2 != 0&lt;br&gt;
    if is_armstrong_num and is_odd:&lt;br&gt;
        properties = ["armstrong", "odd"]&lt;br&gt;
    elif is_armstrong_num and not is_odd:&lt;br&gt;
        properties = ["armstrong", "even"]&lt;br&gt;
    elif is_odd:&lt;br&gt;
        properties = ["odd"]&lt;br&gt;
    else:&lt;br&gt;
        properties = ["even"]&lt;br&gt;
    return {&lt;br&gt;
        "number": parsed_number,&lt;br&gt;
        "is_prime": is_prime(abs_number),&lt;br&gt;
        "is_perfect": False,  # No perfect number check in this version&lt;br&gt;
        "properties": properties,&lt;br&gt;
        "digit_sum": digit_sum(abs_number),&lt;br&gt;
        "fun_fact": get_fun_fact(abs_number),&lt;br&gt;
    }&lt;/p&gt;

&lt;h1&gt;
  
  
  Run the app with Uvicorn
&lt;/h1&gt;

&lt;p&gt;if &lt;strong&gt;name&lt;/strong&gt; == "&lt;strong&gt;main&lt;/strong&gt;":&lt;br&gt;
    import uvicorn&lt;br&gt;
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)`&lt;/p&gt;

&lt;p&gt;This code processes a number, classifies its properties, and fetches a fun fact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RUNNING AND TESTING THE API LOCALLY&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;To start the FastAPI server, I ran the following command:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;uvicorn main:app --host 0.0.0.0 --port 8000&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72re0n496z5psbxpqx6b.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72re0n496z5psbxpqx6b.PNG" alt=" " width="719" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I tested the API using a browser by navigating to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://127.0.0.1:8000/docs" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/docs&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The test was successful, indicated by the output below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0s1sivldcm76d7ni45bs.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0s1sivldcm76d7ni45bs.PNG" alt=" " width="724" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SETTING UP GITHUB&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I initialized a Git repository, committed my files, and pushed them to GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;git init&lt;br&gt;
git add .&lt;br&gt;
git commit -m "Initial commit"&lt;br&gt;
git branch -M main&lt;br&gt;
git remote add origin &lt;a href="https://github.com/OmachokoYakubu/HNG-12-DevOps-Projects" rel="noopener noreferrer"&gt;https://github.com/OmachokoYakubu/HNG-12-DevOps-Projects&lt;/a&gt;&lt;br&gt;
git push -u origin main&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj9z76f8mivex2r3rdw1.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj9z76f8mivex2r3rdw1.PNG" alt=" " width="713" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This ensures my code is version-controlled and publicly available. There was an initial struggle with authentication because GitHub doesn’t accept SSH connections, but I went past that by generating an access token, which resolved the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DEPLOYING THE FASTAPI API ON AWS&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Having completed the above configurations, my next step was to deploy the FastAPI on AWS. This required creating an EC2 instance that I named **HNG12 API Server**. I Chose Ubuntu 22.04 LTS (free tier eligible); t3.micro was already set by default for my region, then I used the following network configurations:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Allow inbound traffic for SSH (port 22).&lt;br&gt;
Allow HTTP (port 80) and HTTPS (port 443) for web access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2u4fxm3brapeirk0xb3e.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2u4fxm3brapeirk0xb3e.PNG" alt=" " width="800" height="202"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxbsebgkkdw46r6s4jty.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxbsebgkkdw46r6s4jty.PNG" alt=" " width="657" height="271"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6ifhcmbpeiya1hsc5mn.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6ifhcmbpeiya1hsc5mn.PNG" alt=" " width="796" height="424"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4bx912q104sgma0eljt.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4bx912q104sgma0eljt.PNG" alt=" " width="756" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After launching, the instance was assigned the following IP address: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13.60.180.189&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50s6tkcvmfv217u3lp21.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50s6tkcvmfv217u3lp21.PNG" alt=" " width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since SSH access was configured as described above, I proceeded to access the server using the following command: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ssh -i ~/.ssh/HNG-12APIServerKP.pem &lt;a href="mailto:ubuntu@13.60.180.189"&gt;ubuntu@13.60.180.189&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5nt3mcb8k0vtaanibim4.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5nt3mcb8k0vtaanibim4.PNG" alt=" " width="711" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gave me instant access to the AWS EC2 instance, where I installed the following dependencies: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade -y&lt;br&gt;
sudo apt install python3 python3-pip git curl nginx -y&lt;br&gt;
pip install fastapi uvicorn requests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The commands above effectively enabled Python, pip, git, curl, NGINX and FastAPI in the EC2 instance deployed on AWS. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvnxibf8vajx2pvbb5g4.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvnxibf8vajx2pvbb5g4.PNG" alt=" " width="722" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TRANSFER OF API CODE FROM LOCAL VM TO AWS&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In order to transfer the API code from my local VM to AWS, I cloned the GitHub repository previously configured using the following command: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;git clone &lt;a href="https://github.com/OmachokoYakubu/HNG-12-DevOps-Projects" rel="noopener noreferrer"&gt;https://github.com/OmachokoYakubu/HNG-12-DevOps-Projects&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F506xn44mrjvue1q9o7qx.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F506xn44mrjvue1q9o7qx.PNG" alt=" " width="713" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I navigated in to the repository’s directory to test the functionality of the API on AWS, using the following command:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;uvicorn main:app --host 0.0.0.0 --port 8000&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It worked perfectly!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffu508yhxaknkdqw46yfh.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffu508yhxaknkdqw46yfh.PNG" alt=" " width="724" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CONFIGURING NGINX AS A REVERSE PROXY&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Configuring nginx as a reverse proxy required the following crucial steps:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Creating a Configuration File&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I navigated to the following directory, and issued the accompanying command as follows: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;cd /etc/nginx/sites-available&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;sudo nano hng12.conf&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Afterwards, I inputted the following program:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;server {&lt;br&gt;
    listen 80;&lt;br&gt;
    server_name 13.60.180.189; # Your public IP or domain name&lt;br&gt;
    location / {&lt;br&gt;
        proxy_pass http://127.0.0.1:8000; # Your API's address and port&lt;br&gt;
        proxy_set_header Host $host;&lt;br&gt;
        proxy_set_header X-Real-IP $remote_addr;&lt;br&gt;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br&gt;
        proxy_set_header X-Forwarded-Proto $scheme;&lt;br&gt;
    }&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
    In order for the NGINX server to run smoothly, I confirmed that the main NGINX configuration files included the configuration file above using the following command: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cat /etc/nginx/nginx.conf&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The output of the command above is as follows:&lt;/p&gt;

&lt;p&gt;`user www-data;&lt;br&gt;
worker_processes auto;&lt;br&gt;
pid /run/nginx.pid;&lt;br&gt;
include /etc/nginx/modules-enabled/&lt;em&gt;.conf;&lt;br&gt;
events {&lt;br&gt;
    worker_connections 768;&lt;br&gt;
    # multi_accept on;&lt;br&gt;
}&lt;br&gt;
http {&lt;br&gt;
    ##&lt;br&gt;
    # Basic Settings&lt;br&gt;
    ##&lt;br&gt;
    sendfile on;&lt;br&gt;
    tcp_nopush on;&lt;br&gt;
    types_hash_max_size 2048;&lt;br&gt;
    # server_tokens off;&lt;br&gt;
    # server_names_hash_bucket_size 64;&lt;br&gt;
    # server_name_in_redirect off;&lt;br&gt;
    include /etc/nginx/mime.types;&lt;br&gt;
    default_type application/octet-stream;&lt;br&gt;
    ##&lt;br&gt;
    # SSL Settings&lt;br&gt;
    ##&lt;br&gt;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE&lt;br&gt;
    ssl_prefer_server_ciphers on;&lt;br&gt;
    ##&lt;br&gt;
    # Logging Settings&lt;br&gt;
    ##&lt;br&gt;
    access_log /var/log/nginx/access.log;&lt;br&gt;
    error_log /var/log/nginx/error.log;&lt;br&gt;
    ##&lt;br&gt;
    # Gzip Settings&lt;br&gt;
    ##&lt;br&gt;
    gzip on;&lt;br&gt;
    # gzip_vary on;&lt;br&gt;
    # gzip_proxied any;&lt;br&gt;
    # gzip_comp_level 6;&lt;br&gt;
    # gzip_buffers 16 8k;&lt;br&gt;
    # gzip_http_version 1.1;&lt;br&gt;
    # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;&lt;br&gt;
    ##&lt;br&gt;
    # Virtual Host Configs&lt;br&gt;
    ##&lt;br&gt;
    include /etc/nginx/conf.d/&lt;/em&gt;.conf;&lt;br&gt;
    &lt;em&gt;include /etc/nginx/sites-enabled/hng12.conf;&lt;/em&gt; # line 60&lt;br&gt;
}&lt;/p&gt;

&lt;h1&gt;
  
  
  mail {
&lt;/h1&gt;

&lt;h1&gt;
  
  
  # See sample authentication script at:
&lt;/h1&gt;

&lt;h1&gt;
  
  
  # &lt;a href="http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript" rel="noopener noreferrer"&gt;http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript&lt;/a&gt;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  # auth_http localhost/auth.php;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  # pop3_capabilities "TOP" "USER";
&lt;/h1&gt;

&lt;h1&gt;
  
  
  # imap_capabilities "IMAP4rev1" "UIDPLUS";
&lt;/h1&gt;

&lt;h1&gt;
  
  
  server {
&lt;/h1&gt;

&lt;h1&gt;
  
  
  listen     localhost:110;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  protocol   pop3;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  proxy      on;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  }
&lt;/h1&gt;

&lt;h1&gt;
  
  
  server {
&lt;/h1&gt;

&lt;h1&gt;
  
  
  listen     localhost:143;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  protocol   imap;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  proxy      on;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  }
&lt;/h1&gt;

&lt;h1&gt;
  
  
  }`
&lt;/h1&gt;

&lt;p&gt;Line 60 above (in italics) includes the configuration file which makes nginx a reverse proxy. The nginx test proved that the configurations above were correct.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzi6mzvo7f7u4kqpmoq5r.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzi6mzvo7f7u4kqpmoq5r.PNG" alt=" " width="724" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TESTING THE LIVE API&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;To finally determine that the live API is fully functional, I tested it on a browser using the folllowing URL:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://13.60.180.189/api/classify-number?number=371" rel="noopener noreferrer"&gt;http://13.60.180.189/api/classify-number?number=371&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It was a resounding success!!!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzj8qei73p7id1lao38x.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzj8qei73p7id1lao38x.PNG" alt=" " width="800" height="79"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6xwtufko68z7xtzimee.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6xwtufko68z7xtzimee.PNG" alt=" " width="526" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Further tests with negative integers and non-integers gave unique results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6479h5hbrq1ugps6ea4.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6479h5hbrq1ugps6ea4.PNG" alt=" " width="782" height="207"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F856cm01mo1v10vsfx14q.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F856cm01mo1v10vsfx14q.PNG" alt=" " width="562" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CREATION OF README.md&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The climax of this project was the creation of the README.md file. I navigated into the project directory on the AWS instance, and issued the following command: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;nano README.md&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I followed that up by pasting the following instructions into the nano text editor: &lt;/p&gt;

&lt;h1&gt;
  
  
  Number Classification API
&lt;/h1&gt;

&lt;p&gt;This API takes a number as input and returns interesting mathematical properties about it, along with a fun fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Checks if the number is &lt;strong&gt;Prime&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Determines if the number is an &lt;strong&gt;Armstrong number&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Classifies as &lt;strong&gt;Odd or Even&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Calculates the &lt;strong&gt;Sum of Digits&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Retrieves a &lt;strong&gt;Fun Fact&lt;/strong&gt; from the Numbers API&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📌 Technologies Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; - Python framework for building APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uvicorn&lt;/strong&gt; - ASGI server to run the FastAPI application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requests&lt;/strong&gt; - For fetching data from the Numbers API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx&lt;/strong&gt; - Reverse proxy for handling HTTP requests&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📌 API Endpoints
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ &lt;strong&gt;Classify a Number&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;URL:&lt;/strong&gt; &lt;code&gt;GET /api/classify-number?number=&amp;lt;int&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;https://13.60.180.189/api/classify-number?number=371&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Response (200 OK)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
json
  {
    "number": 371,
    "is_prime": false,
    "is_perfect": false,
    "properties": ["armstrong", "odd"],
    "digit_sum": 11,
    "fun_fact": "371 is an Armstrong number because 3^3 + 7^3 + 1^3 = 371"
  }
#Response (400 Bad Request)
{
  "number": "alphabet",
  "error": true
}

#📌 How to Install and Run Locally
#Clone the Repository
git clone https://github.com/OmachokoYakubu/HNG-12-DevOps-Projects.git
cd HNG-12-DevOps-Projects

#Install Dependencies
**pip install fastapi uvicorn requests**

#Run the Server
**uvicorn main:app --host 0.0.0.0 --port 8000**

#Access the API Documentation
Open **http://127.0.0.1:8000/docs** in your browser

#📌 Deployment on AWS
#Launch an EC2 Instance (Ubuntu 22.04 LTS)
#SSH into the instance
**ssh -i ~/.ssh/HNG-12APIServerKP.pem ubuntu@13.60.180.189**

#Clone this repository and install dependencies
#Run Uvicorn to start the API
#Configure Nginx as a reverse proxy
#Access the API using your public IP!#

# Live Deployment
Public API URL: **https://13.60.180.189/api/classify-number?number=371(alternate with different numbers to test output)**

#Author **Omachoko Yakubu**
GitHub: **OmachokoYakubu**


After writing the README.md file, I committed and pushed it to GitHub using the following commands:

**git add README.md
git commit -m "Added README file with API documentation"
git push origin main**

The README.md now provides full API documentation and deployment details




&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
    </item>
    <item>
      <title>DEPLOYING NGINX ON AWS EC2: MY HNG12 STAGE 0 EXPERIENCE</title>
      <dc:creator>Omachoko Tanimu Yakubu</dc:creator>
      <pubDate>Fri, 31 Jan 2025 20:24:50 +0000</pubDate>
      <link>https://dev.to/omachokoyakubu/deploying-nginx-on-aws-ec2-my-hng12-stage-0-experience-3di5</link>
      <guid>https://dev.to/omachokoyakubu/deploying-nginx-on-aws-ec2-my-hng12-stage-0-experience-3di5</guid>
      <description>&lt;p&gt;&lt;strong&gt;INTRODUCTION&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As part of the HNG12 internship (under the DevOps track), I was tasked with deploying an NGINX web server on any cloud service of my choice. I chose Amazon Web Services and setup an Ubuntu Server EC2 instance. This involved launching the virtual machine, configuring security rules, and automating the installation of NGINX. In this blog post, I’ll walk you through my experience, the challenges I faced, and my key takeaways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SETTING UP THE EC2 INSTANCE&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Launching an Ubuntu Server on AWS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To begin, I created a free tier account and logged into AWS Management Console, then I navigated to EC2. I then clicked "Launch Instance", where I configured the following:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ubuntu Server 24.04 LTS as the operating system  Amazon Machine Image (AMI)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00hbvcnpyyblft7cluxu.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00hbvcnpyyblft7cluxu.PNG" alt=" " width="800" height="481"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftox9fvx3eca93gcp0zh3.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftox9fvx3eca93gcp0zh3.PNG" alt=" " width="658" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;t3.micro instance (Free Tier eligible)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdg0exwphqxmqfh32vb8.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdg0exwphqxmqfh32vb8.PNG" alt=" " width="794" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8 GiB of storage (gp3 SSD)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5nff9obg7vncs6rn0pk.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc5nff9obg7vncs6rn0pk.PNG" alt=" " width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enabled Auto-assign Public IP so my server could be accessed from the internet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8ir0u24edw1xdb6d56d.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8ir0u24edw1xdb6d56d.PNG" alt=" " width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Configuring Security Groups&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since the instructions strictly required only HTTP (port 80) access, the following custom security group with the following rule was created:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inbound Rule&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;HTTP (Port 80) → Source: 0.0.0.0/0 (anywhere on the internet).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outbound Rule&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;All traffic allowed (default AWS setting).&lt;br&gt;
I did not enable SSH (port 22) since it was not allowed per the instructions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AUTOMATING NGINX INSTALLATION&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since SSH access was blocked, I couldn’t manually install NGINX. Instead, I used User Data, a feature that runs commands automatically when an instance is created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Configuring NGINX in User Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before launching the instance, I scrolled down to Advanced Details → User Data and pasted the following script:&lt;/p&gt;

&lt;h3&gt;
  
  
  !/bin/bash
&lt;/h3&gt;

&lt;p&gt;sudo apt update -y&lt;br&gt;
sudo apt install nginx -y echo 'Welcome to DevOps Stage 0 - Omachoko Yakubu/ChockqOteewy' | sudo tee /var/www/html/index.html&lt;br&gt;
sudo systemctl restart nginx&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration of NGINX using User Data under Advanced Settings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhz21zxl4bsqrvqsie0v.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhz21zxl4bsqrvqsie0v.PNG" alt=" " width="549" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This script:&lt;/p&gt;

&lt;p&gt;Installs NGINX.&lt;/p&gt;

&lt;p&gt;Creates an index.html file with a custom message.&lt;/p&gt;

&lt;p&gt;Ensures NGINX restarts so the changes take effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Details of Instance After Launching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfwlh25vlk2vi7cm0h4t.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfwlh25vlk2vi7cm0h4t.PNG" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VERIFYING THE DEPLOYMENT&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Checking the NGINX Web Page&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After launching my instance, I copied its Public IPv4 Address from the AWS EC2 Dashboard and visited the public address of my server:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://13.60.179.5/" rel="noopener noreferrer"&gt;http://13.60.179.5/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the setup was successful, the browser displayed the following message:&lt;/p&gt;

&lt;p&gt;Welcome to DevOps Stage 0 - Omachoko Yakubu/ChockqOteewy &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6yvn4yr4l4ut0jkeb4t9.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6yvn4yr4l4ut0jkeb4t9.PNG" alt=" " width="426" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success! My NGINX web server was running perfectly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CHALLENGES AND HOW I OVERCAME THEM&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A major challenge I faced while setting up NGINX on an Ubuntu Server in AWS was thinking I needed to connect to the VM manually before running the script. I later discovered that I could input the script directly as part of the initial setup in the Advanced Details section under User Data, which made the process much easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KEY TAKEAWAYS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I learned how to deploy and configure NGINX on AWS without SSH access.&lt;/p&gt;

&lt;p&gt;I practiced using User Data to automate server setup.&lt;/p&gt;

&lt;p&gt;I gained a deeper understanding of AWS EC2 security groups and instance management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;USEFUL RESOURCES&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Learn more about DevOps Engineers: &lt;a href="https://hng.tech/hire/devops-engineers" rel="noopener noreferrer"&gt;https://hng.tech/hire/devops-engineers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Explore AWS Solutions Architects: &lt;a href="https://hng.tech/hire/aws-solutions-architects" rel="noopener noreferrer"&gt;https://hng.tech/hire/aws-solutions-architects&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
