# Manual Worker Setup Guide

> **⚠️ CLI Issues**: The `stackscope-worker init` command fails in non-interactive environments (CI/CD, Claude Code, automated tooling). This guide provides manual setup instructions.

## 🚨 Known CLI Issues

- **ERR_USE_AFTER_CLOSE**: CLI uses inquirer prompts that don't work in non-interactive terminals
- **ERR_TTY_INIT_FAILED**: create-cloudflare package requires a real TTY
- **--yes flag doesn't work**: Creates empty directories without generating files
- **Brittle error handling**: Fails silently, leaves incomplete state

## 🛠️ Manual Setup (Recommended)

### Step 1: Create Worker Directory Structure

```bash
mkdir my-stackscope-worker
cd my-stackscope-worker

# Create basic structure
mkdir -p src
```

### Step 2: Create wrangler.toml

```toml
# wrangler.toml
name = "my-stackscope-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]

[env.production]
name = "my-stackscope-worker"

[env.development] 
name = "my-stackscope-worker-dev"

# Environment variables (set via wrangler secret)
[vars]
# GITHUB_WEBHOOK_SECRET - set via: wrangler secret put GITHUB_WEBHOOK_SECRET
# CORS_ORIGINS - optional, defaults to allow all
```

### Step 3: Create Worker Code

```javascript
// src/index.js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const path = url.pathname;

    // CORS headers
    const corsHeaders = {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    };

    // Handle OPTIONS requests
    if (request.method === 'OPTIONS') {
      return new Response(null, { headers: corsHeaders });
    }

    // Health check endpoint
    if (path === '/health') {
      return new Response(JSON.stringify({ status: 'ok' }), {
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      });
    }

    // Browser logs endpoint
    if (path === '/webhook/browser' && request.method === 'POST') {
      try {
        const logs = await request.text();
        
        // Process each line as NDJSON
        const logLines = logs.trim().split('\n');
        for (const line of logLines) {
          if (line.trim()) {
            const logData = JSON.parse(line);
            console.log(JSON.stringify({
              ...logData,
              source: 'browser',
              worker_timestamp: new Date().toISOString()
            }));
          }
        }

        return new Response('OK', { headers: corsHeaders });
      } catch (error) {
        console.error('Browser log processing error:', error);
        return new Response('Error processing logs', { 
          status: 500, 
          headers: corsHeaders 
        });
      }
    }

    // GitHub webhook endpoint  
    if (path === '/webhook/github' && request.method === 'POST') {
      // Require specific headers
      const event = request.headers.get('X-GitHub-Event');
      const delivery = request.headers.get('X-GitHub-Delivery');
      
      if (!event) {
        return new Response('Missing X-GitHub-Event header', { 
          status: 400, 
          headers: corsHeaders 
        });
      }
      
      if (!delivery) {
        return new Response('Missing X-GitHub-Delivery header', { 
          status: 400, 
          headers: corsHeaders 
        });
      }

      try {
        // Handle both JSON and form-encoded payloads
        let payload;
        const contentType = request.headers.get('Content-Type');
        
        if (contentType && contentType.includes('application/json')) {
          payload = await request.json();
        } else {
          // Form-encoded (GitHub's default)
          const formData = await request.formData();
          const payloadStr = formData.get('payload');
          payload = payloadStr ? JSON.parse(payloadStr) : {};
        }

        // Safe access to nested properties
        const pusher = payload.pusher || {};
        const repository = payload.repository || {};
        
        console.log(JSON.stringify({
          level: 'info',
          msg: `GitHub ${event} event`,
          source: 'github',
          event,
          delivery,
          repository: repository.full_name || 'unknown',
          pusher: pusher.name || pusher.login || 'unknown',
          timestamp: new Date().toISOString(),
          payload: payload
        }));

        return new Response('Webhook processed', { headers: corsHeaders });
      } catch (error) {
        console.error('GitHub webhook error:', error);
        return new Response('Webhook processing failed', { 
          status: 500, 
          headers: corsHeaders 
        });
      }
    }

    return new Response('Not Found', { 
      status: 404, 
      headers: corsHeaders 
    });
  }
};
```

### Step 4: Create package.json

```json
{
  "name": "my-stackscope-worker",
  "version": "1.0.0",
  "description": "StackScope Cloudflare Worker",
  "main": "src/index.js",
  "scripts": {
    "dev": "wrangler dev",
    "deploy": "wrangler deploy",
    "tail": "wrangler tail",
    "test": "node test-worker.js"
  },
  "devDependencies": {
    "wrangler": "^3.0.0"
  },
  "keywords": ["stackscope", "cloudflare", "worker", "logging"]
}
```

### Step 5: Deploy Worker

```bash
# Install dependencies
npm install

# Login to Cloudflare (if not already done)
wrangler auth login

# Deploy to production
wrangler deploy

# Get worker URL from output:
# https://my-stackscope-worker.your-account.workers.dev
```

### Step 6: Test Worker

```bash
# Test health endpoint
curl https://my-stackscope-worker.your-account.workers.dev/health

# Test browser logs
curl -X POST https://my-stackscope-worker.your-account.workers.dev/webhook/browser \
  -H "Content-Type: text/plain" \
  -d '{"level":"info","msg":"test log","ts":"2024-01-01T00:00:00.000Z"}'

# Test GitHub webhook
curl -X POST https://my-stackscope-worker.your-account.workers.dev/webhook/github \
  -H "X-GitHub-Event: push" \
  -H "X-GitHub-Delivery: 12345" \
  -H "Content-Type: application/json" \
  -d '{"pusher":{"name":"test"},"repository":{"full_name":"test/repo"}}'
```

## 🔍 Troubleshooting

### Deployment Issues

```bash
# Check wrangler configuration
wrangler whoami

# Validate wrangler.toml
wrangler dev --local

# Check deployment status
wrangler deployments list
```

### Worker Logs

```bash
# Stream live logs
wrangler tail

# Filter logs
wrangler tail --format json | jq 'select(.level == "error")'
```

### Environment Variables

```bash
# Set secrets (for webhook signature validation)
wrangler secret put GITHUB_WEBHOOK_SECRET
# Enter your secret when prompted

# List current vars
wrangler secret list
```

## ✅ Verification

After setup, verify everything works:

1. ✅ Health endpoint returns `{"status":"ok"}`
2. ✅ Browser logs endpoint accepts POST with JSON
3. ✅ GitHub webhook endpoint handles both content types
4. ✅ Worker logs appear in `wrangler tail`
5. ✅ No CORS errors when calling from browser

## 🚀 Next Steps

Once your worker is deployed:

1. Copy the worker URL
2. Set `VITE_STACKSCOPE_WORKER_URL` in your app
3. Initialize StackScope SDK with `createStackScope()`
4. Logs will automatically flow to your worker

## 📚 Alternative Approaches

### Option 1: Use Template Repository
```bash
git clone https://github.com/JRGCr/stackscope-worker-template
cd stackscope-worker-template
# Edit wrangler.toml with your account details
wrangler deploy
```

### Option 2: Serverless Functions
If Cloudflare Workers don't work for you, StackScope can send logs to:
- Vercel Functions
- Netlify Functions  
- AWS Lambda
- Any HTTP endpoint that accepts POST with JSON

Just set the `endpoint` instead of `workerUrl` in StackScope config.