← Back to Blog
Tunneling, File Viewer and Developer Tools
13 min read tunneling developer-tools monaco-editor

Tunneling, File Viewer & Developer Tools in OpenACP

AI coding agents do not just generate code -- they build and run applications, serve local development servers, and produce files you need to review. OpenACP includes a suite of developer tools that bridge the gap between your local development environment and your messaging app: tunnel providers to expose local ports, a Monaco-powered file viewer for syntax-highlighted code review, usage tracking for API cost management, and diagnostic commands that help you troubleshoot quickly. This guide covers each tool in depth.

Tunnel Providers: Expose Local Ports to the World

When your AI agent spins up a local development server -- say, a React app on port 3000 or a FastAPI service on port 8000 -- you usually need to be at your computer to see it in a browser. But if you are controlling the agent from Telegram on your phone, how do you preview the running application?

OpenACP solves this with built-in tunnel providers that create secure public URLs pointing to your local ports. When the agent starts a dev server, OpenACP can automatically create a tunnel and send you the public URL in your chat. You tap the link on your phone and see your app running live.

OpenACP supports four tunnel providers, each with different characteristics:

Cloudflare Tunnel (cloudflared)

Cloudflare Tunnel is the recommended default. It is free for development use, requires no account for quick tunnels, provides fast global edge connectivity, and automatically handles HTTPS.

# Install cloudflared
brew install cloudflared     # macOS
sudo apt install cloudflared # Ubuntu/Debian

# OpenACP configuration
{
  "tunnel": {
    "provider": "cloudflare"
  }
}

When the agent starts a server on a local port, OpenACP runs cloudflared tunnel --url http://localhost:PORT in the background and captures the generated URL. The URL is sent to your chat as a clickable link.

// How OpenACP creates a Cloudflare tunnel
async function createCloudflareTunnel(port: number): Promise<string> {
  const proc = spawn('cloudflared', ['tunnel', '--url', `http://localhost:${port}`]);

  return new Promise((resolve) => {
    proc.stderr.on('data', (data: Buffer) => {
      const output = data.toString();
      // Cloudflare outputs the URL to stderr
      const match = output.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/);
      if (match) {
        resolve(match[0]);
      }
    });
  });
}

Cloudflare tunnels generate random subdomains like https://orange-butterfly-3f42.trycloudflare.com. These URLs are temporary and expire when the tunnel process exits.

ngrok

ngrok is a popular alternative that offers more features like custom domains, request inspection, and replay. It requires a free account and auth token.

# Install ngrok
brew install ngrok  # macOS

# Authenticate (one-time)
ngrok config add-authtoken YOUR_AUTH_TOKEN

# OpenACP configuration
{
  "tunnel": {
    "provider": "ngrok",
    "authToken": "your_ngrok_auth_token"
  }
}

ngrok provides a local dashboard at http://127.0.0.1:4040 where you can inspect all HTTP requests passing through the tunnel. This is incredibly useful for debugging webhook integrations or API calls.

bore

bore is a lightweight, open-source alternative written in Rust. It is the simplest option -- a single binary with no authentication required. It is ideal for quick, ephemeral tunnels when you do not want to set up accounts or install heavy dependencies.

# Install bore
cargo install bore-cli

# OpenACP configuration
{
  "tunnel": {
    "provider": "bore"
  }
}

bore generates URLs in the format bore.pub:PORT. It does not provide HTTPS by default, so it is best suited for development and testing rather than production previews.

Tailscale Funnel

If you are already using Tailscale for your network, Tailscale Funnel provides tunnel functionality that integrates with your existing Tailscale setup. It uses your Tailscale domain and provides HTTPS automatically.

# Tailscale must be installed and authenticated
# Enable funnel for a port
tailscale funnel 3000

# OpenACP configuration
{
  "tunnel": {
    "provider": "tailscale"
  }
}

Tailscale Funnel is unique because the URL is stable -- it uses your machine's Tailscale hostname, so the same URL works across sessions. This is useful for ongoing development where you want a consistent preview URL.

Choosing a Tunnel Provider

Provider HTTPS Account Required Custom Domain Best For
Cloudflare Yes No (quick tunnels) With account General use (recommended)
ngrok Yes Yes (free tier) Yes (paid) Request inspection, debugging
bore No No No Minimal, lightweight, quick
Tailscale Yes Yes (Tailscale) Yes (stable) Tailscale users, stable URLs

Built-in File Viewer with Monaco Editor

When your AI agent creates or modifies a file, you often want to review the changes before proceeding. Reading raw code in a Telegram message is not a great experience -- no syntax highlighting, no line numbers, no ability to scroll through large files. OpenACP addresses this with a built-in file viewer powered by Monaco Editor (the same editor engine that powers VS Code).

When the agent produces a file or you request to view a specific file, OpenACP serves a local web page with the file rendered in Monaco Editor. You receive a link in your chat that opens a full-featured code viewer with:

// The file viewer is served by OpenACP's built-in Hono web server
import { Hono } from 'hono';
import { serveStatic } from 'hono/serve-static';

const app = new Hono();

// Serve Monaco Editor assets
app.use('/monaco/*', serveStatic({ root: './node_modules/monaco-editor/min' }));

// Serve file viewer page
app.get('/view/:sessionId/:filePath', async (c) => {
  const { sessionId, filePath } = c.req.param();
  const session = sessions.get(sessionId);

  if (!session) return c.text('Session not found', 404);

  const content = await fs.readFile(filePath, 'utf-8');
  const language = detectLanguage(filePath);

  return c.html(renderMonacoViewer({
    content,
    language,
    filePath,
    readOnly: true
  }));
});

Accessing the File Viewer

There are two ways to use the file viewer:

Automatic: When the agent creates or modifies a file, OpenACP can automatically generate a viewer link and include it in the response message. You click the link to see the file with full syntax highlighting.

On-demand: Send a message like "show me the file src/utils/parser.ts" and the agent will respond with a viewer link for that specific file.

When tunneling is configured, the file viewer URL is accessible from your phone. Without tunneling, it is only accessible on your local network (typically http://localhost:21420/view/...).

Diff View

The file viewer also supports a diff mode that shows the before and after of a file modification side by side. This uses Monaco's built-in diff editor, providing the same diff experience you would see in VS Code:

// Diff viewer endpoint
app.get('/diff/:sessionId/:filePath', async (c) => {
  const { sessionId, filePath } = c.req.param();
  const session = sessions.get(sessionId);

  const original = session.getOriginalContent(filePath);
  const modified = await fs.readFile(filePath, 'utf-8');

  return c.html(renderMonacoDiffViewer({
    original,
    modified,
    language: detectLanguage(filePath),
    filePath
  }));
});

Additions appear highlighted in green, deletions in red, and unchanged lines in the default color. This makes it easy to review exactly what the agent changed, even for large files.

Usage Tracking and Budget Limits

AI coding agents consume API tokens, and those tokens cost money. Without visibility into usage, costs can spiral unexpectedly, especially in team environments where multiple developers are running sessions simultaneously. OpenACP provides built-in usage tracking that monitors API consumption and enforces budget limits.

How Usage Tracking Works

OpenACP intercepts the token usage metadata from agent responses (most AI providers include token counts in their API responses) and aggregates it per session, per user, and per time period:

// Usage tracking data structure
interface UsageRecord {
  sessionId: string;
  userId: string;
  platform: 'telegram' | 'discord' | 'slack';
  agent: string;
  inputTokens: number;
  outputTokens: number;
  estimatedCost: number;
  timestamp: Date;
}

// Usage is tracked per session and aggregated
class UsageTracker {
  private records: UsageRecord[] = [];

  async trackUsage(session: Session, tokens: TokenUsage) {
    const cost = this.estimateCost(session.agent, tokens);

    this.records.push({
      sessionId: session.id,
      userId: session.userId,
      platform: session.platform,
      agent: session.agentType,
      inputTokens: tokens.input,
      outputTokens: tokens.output,
      estimatedCost: cost,
      timestamp: new Date()
    });

    // Check budget limits
    await this.checkBudgetLimits(session.userId);
  }

  async getDailyUsage(userId: string): Promise<UsageSummary> {
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const todayRecords = this.records.filter(r =>
      r.userId === userId && r.timestamp >= today
    );

    return {
      totalInputTokens: sum(todayRecords, 'inputTokens'),
      totalOutputTokens: sum(todayRecords, 'outputTokens'),
      totalCost: sum(todayRecords, 'estimatedCost'),
      sessionCount: new Set(todayRecords.map(r => r.sessionId)).size
    };
  }
}

Setting Budget Limits

You can configure daily and monthly budget limits to prevent unexpected costs:

{
  "usage": {
    "tracking": true,
    "budgets": {
      "daily": 10.00,
      "monthly": 200.00,
      "perSession": 5.00
    },
    "currency": "USD"
  }
}

When a budget limit is approaching (80% consumed), OpenACP sends a warning message. When the limit is reached, new sessions are paused or throttled depending on your configuration.

Viewing Usage Reports

You can request usage reports directly in your chat:

Usage data is also available through the REST API (covered in the daemon mode guide) for integration with external monitoring dashboards.

Doctor Diagnostics

We touched on openacp doctor in the security guide, but it deserves a deeper look as a developer tool. The doctor command performs a comprehensive check of your OpenACP installation and configuration:

$ openacp doctor

OpenACP Doctor v1.4.2
=====================

System Checks
  [PASS] Node.js v22.5.0 (>= 20.0.0 required)
  [PASS] npm v10.8.0
  [PASS] OS: darwin arm64

Configuration
  [PASS] Config file: ~/.openacp/config.json
  [PASS] Config valid (Zod schema check)
  [PASS] File permissions: 600

Platform Connectivity
  [PASS] Telegram Bot API: connected (bot: @MyOpenACPBot)
  [PASS] Discord Gateway: connected (bot: OpenACP#1234)
  [SKIP] Slack: not configured

Agent Availability
  [PASS] claude: found at /usr/local/bin/claude
  [PASS] gemini: found at /usr/local/bin/gemini
  [WARN] codex: not found in PATH

Tunnel Providers
  [PASS] cloudflared: v2024.8.2
  [SKIP] ngrok: not installed
  [SKIP] bore: not installed
  [PASS] tailscale: v1.68.1

Voice
  [PASS] Groq API: connected
  [PASS] Edge TTS: available

Security
  [PASS] Allowed user IDs configured
  [WARN] Auto-approve is ENABLED
  [PASS] Max sessions: 10
  [PASS] Session timeout: 60 min

Network
  [PASS] Port 21420: available
  [PASS] Outbound HTTPS: working

Summary: 18 passed, 2 warnings, 0 errors, 2 skipped

The doctor command is invaluable for troubleshooting. If something is not working, openacp doctor will usually point you to the exact issue -- a missing binary, an invalid token, a port conflict, or a misconfigured permission.

Running Doctor in CI/CD

You can also use the doctor command in your deployment pipeline to verify that the environment is correctly set up before starting OpenACP:

# In your deployment script
openacp doctor --json | jq '.errors'
if [ $? -ne 0 ]; then
  echo "OpenACP doctor found errors, aborting deployment"
  exit 1
fi

openacp --daemon start

The --json flag outputs structured JSON that can be parsed by scripts and monitoring tools.

CLI Commands Reference

OpenACP provides a comprehensive set of CLI commands for managing your installation. Here is the complete reference:

Core Commands

# Start OpenACP (interactive mode)
openacp

# Start with a specific config file
openacp --config /path/to/config.json

# Start with a specific working directory
openacp --cwd /path/to/project

# Start with a specific agent
openacp --agent claude
openacp --agent gemini
openacp --agent codex

Daemon Commands

# Start as background daemon
openacp --daemon start

# Stop the daemon
openacp --daemon stop

# Check daemon status
openacp --daemon status

# View daemon logs
openacp --daemon logs
openacp --daemon logs --follow  # tail -f style

Diagnostic Commands

# Run diagnostics
openacp doctor

# JSON output for scripting
openacp doctor --json

# Version information
openacp --version

# Help
openacp --help

Session Commands (via chat)

These commands are sent as messages in your chat, not from the terminal:

/handoff     -- Generate a session handoff token
/resume XXX  -- Resume a session from a handoff token
/usage       -- Show usage statistics
/cancel      -- Cancel the current agent operation
/clear       -- Clear the session and start fresh

Integrating with Your Development Workflow

OpenACP's developer tools are designed to integrate seamlessly with existing workflows rather than replacing them. Here are some integration patterns:

Preview Deployments

When your agent builds a feature, use tunneling to create a preview URL. Share that URL in your team's Slack channel for quick feedback:

You: "Build the new user profile page and start the dev server"

Agent: "Done! I've created the UserProfile component with avatar,
bio, and settings sections. Dev server running on port 3000.

Preview: https://autumn-leaf-4c21.trycloudflare.com"

Code Review from Mobile

Use the Monaco file viewer to review code changes on your phone. The viewer is fully responsive and works well on mobile screens, with horizontal scrolling for wide code and pinch-to-zoom for readability.

Cost Management for Teams

Set per-user daily budgets to prevent any single developer from consuming the team's entire API budget. Use the REST API's usage endpoints to build a dashboard showing team-wide consumption.

Automated Health Checks

Run openacp doctor --json on a cron schedule and alert (via Slack webhook, PagerDuty, etc.) if any checks fail. This catches issues like expired tokens, unavailable agents, or port conflicts before they affect developers.

# crontab entry: run doctor every hour
0 * * * * openacp doctor --json | jq 'select(.errors | length > 0)' \
  && curl -X POST -H 'Content-type: application/json' \
    --data '{"text":"OpenACP doctor found errors!"}' \
    https://hooks.slack.com/services/YOUR/WEBHOOK/URL

Performance Considerations

Some things to keep in mind about resource usage with these developer tools:

Wrapping Up

OpenACP's developer tools transform the messaging-based coding experience from a simple chat interface into a full-featured development environment. Tunnel providers let you preview running applications from anywhere. The Monaco file viewer gives you proper code review capabilities on any device. Usage tracking keeps costs visible and controlled. And the doctor command ensures your setup stays healthy over time.

These tools are not just convenience features -- they address real pain points that arise when you move your AI coding workflow away from the desktop. By solving these problems at the platform level, OpenACP lets you focus on the actual coding work rather than fighting with the tooling.

Developer Tools at Your Fingertips

Install OpenACP and get tunneling, file viewing, and usage tracking out of the box.

npm install -g @openacp/cli && openacp