当我第一次在拥挤的地铁里,用手机敲下"帮我写一个React登录页面",然后看着屏幕上Claude Code一行行输出代码时,我突然意识到:编程这件事,终于可以随身携带了。


引子:一个程序员的通勤噩梦

作为一个资深"996"程序员,我每天花在通勤上的时间大约两小时。这两小时里,我通常会刷刷技术文章、看看B站视频,或者干脆闭目养神。但心里总有个声音在说:要是能把这时间用来写代码多好?

可是——笔记本太重、iPad没有好用的IDE、手机屏幕又太小……各种借口让我一直没能实现"移动办公"的梦想。

直到我发现了 WebCode 这个项目。

它不是一个简单的在线代码编辑器,也不是又一个"可以在浏览器里写代码"的玩具。它是一个真正能在手机上远程驱动AI编程助手的平台——你只需要打开浏览器,输入你的想法,剩下的交给Claude Code或Codex来搞定。

听起来很魔幻?让我带你一探究竟。


一、项目背景:为什么需要一个Web版的AI编程助手?

1.1 AI编程工具的现状与痛点

2024-2025年,AI编程助手如雨后春笋般涌现:Claude Code、OpenAI Codex、GitHub Copilot、Qwen……这些工具正在彻底改变我们写代码的方式。

但问题来了:这些工具大多是命令行工具(CLI),需要在本地环境中运行。

这意味着什么呢?

  • 你需要一台配置不错的电脑

  • 你需要安装各种环境依赖

  • 你需要配置API Key、代理等

  • 最重要的——你必须坐在电脑前

作为一个经常在路上的人,我真的很想问一句:难道在咖啡馆、在地铁上、甚至在马桶上,我就不能让AI帮我写代码了吗?

1.2 WebCode的解决方案

WebCode的核心思路非常简单:

把CLI工具部署在服务器上,通过Web界面远程调用。

就像你用网页版的在线IDE一样,只不过这次敲代码的不是你,而是AI。你负责提需求,AI负责干活,网页负责展示——完美的三角关系。

更妙的是,它支持多种AI CLI工具,通过一套优雅的适配器模式,可以无缝切换Claude Code、Codex等不同的AI助手。今天用Claude,明天换Codex,后天说不定还能接入国产的Qwen——总有一款适合你。


二、技术架构解析:优雅设计的背后

2.1 整体架构概览

先来看看项目的目录结构,感受一下DDD(领域驱动设计)的气息:

WebCodeCli/
├── WebCodeCli/                    # 主项目 (Blazor Server)
│   ├── Components/                # 可复用组件
│   ├── Pages/                     # 页面组件  
│   │   └── CodeAssistant.razor    # 核心编程助手页面
│   └── wwwroot/                   # 静态资源
├── WebCodeCli.Domain/             # 领域层
│   ├── Domain/
│   │   ├── Model/                 # 领域模型
│   │   └── Service/               # 领域服务
│   │       └── Adapters/          # CLI适配器(核心!)
│   └── Repositories/              # 数据仓储
└── cli/                           # CLI工具帮助文档

如果你熟悉企业级应用开发,这个结构一定让你感到亲切。清晰的分层、明确的职责划分——这不是一个周末写着玩的项目,而是一个经过认真设计的产品。

2.2 技术栈选型:为什么是Blazor?

项目采用的技术栈值得好好聊聊:

技术 选型理由
Blazor Server 服务端渲染+SignalR实时通信,完美支持流式输出
Ant Design Blazor 成熟的企业级UI组件库,开箱即用
Monaco Editor VS Code同款编辑器内核,专业级代码展示
SqlSugar ORM 轻量级ORM,支持多数据库切换
Docker 一键部署,环境隔离

你可能会问:为什么选Blazor而不是React/Vue?

我觉得这是一个非常务实的选择。Blazor Server模式的最大优势是——所有代码都在服务端运行。这意味着:

  1. 前后端天然统一:不需要写两套代码、处理CORS、维护API接口

  2. 实时通信开箱即用:SignalR自动管理WebSocket连接

  3. 流式输出天然支持:AI的打字机效果实现起来轻而易举

对于一个需要频繁与后端进程交互、需要实时展示输出的应用来说,Blazor Server简直是天作之合。

2.3 核心设计亮点:适配器模式的优雅运用

如果说整个项目有什么设计让我眼前一亮,那一定是CLI适配器系统

不同的AI CLI工具有着完全不同的:

  • 命令格式:Claude用-p表示print模式,Codex用exec

  • 输出格式:Claude输出stream-json,Codex输出JSONL

  • 会话恢复机制:Claude用--resume,Codex用resume <session_id>

如果不做抽象,代码里会充斥着这样的判断:

if (toolId == "claude") {
    // Claude的逻辑
} else if (toolId == "codex") {
    // Codex的逻辑
} else if (toolId == "qwen") {
    // Qwen的逻辑
}
// ... 噩梦开始

WebCode的解决方案是定义一个统一的适配器接口:

public interface ICliToolAdapter
{
    // 支持哪些工具
    string[] SupportedToolIds { get; }
    
    // 是否支持流式输出解析
    bool SupportsStreamParsing { get; }
    
    // 构建命令行参数
    string BuildArguments(CliToolConfig tool, string prompt, CliSessionContext context);
    
    // 解析输出行
    CliOutputEvent? ParseOutputLine(string line);
    
    // 提取会话ID(用于恢复会话)
    string? ExtractSessionId(CliOutputEvent outputEvent);
    
    // ... 其他方法
}

然后为每个CLI工具实现自己的适配器。比如Claude Code适配器:

public class ClaudeCodeAdapter : ICliToolAdapter
{
    public string[] SupportedToolIds => new[] { "claude-code", "claude" };
    public bool SupportsStreamParsing => true;

    public string BuildArguments(CliToolConfig tool, string prompt, CliSessionContext context)
    {
        var argsBuilder = new StringBuilder();
        argsBuilder.Append("-p --verbose --output-format=stream-json ");
        argsBuilder.Append("--dangerously-skip-permissions ");
        
        // 如果是恢复会话,加上--resume参数
        if (context.IsResume && !string.IsNullOrEmpty(context.CliThreadId))
        {
            argsBuilder.Append($"--resume {context.CliThreadId} ");
        }
        
        argsBuilder.Append($"\"{escapedPrompt}\"");
        return argsBuilder.ToString();
    }
    
    // ... 其他实现
}

这种设计的好处是显而易见的:

  1. 开闭原则:添加新工具只需新增适配器,不修改现有代码

  2. 单一职责:每个适配器只关心自己的CLI工具

  3. 可测试性:适配器可以独立单元测试

  4. 可扩展性:支持运行时动态注册新适配器

// 在工厂里动态注册新适配器
public void RegisterAdapter(ICliToolAdapter adapter)
{
    if (adapter != null && !_adapters.Contains(adapter))
    {
        _adapters.Add(adapter);
    }
}

这意味着什么?你甚至可以开发一个插件系统,让用户自己上传适配器来支持新的CLI工具!


三、核心功能实现:流式输出的艺术

3.1 为什么流式输出很重要?

用过ChatGPT或Claude的朋友都知道,AI的回复是一个字一个字蹦出来的,像打字机一样。这不仅仅是为了好看——它大大提升了用户体验

想象一下,你问AI一个问题,然后盯着空白的屏幕等30秒,最后"唰"地一下全部显示出来。这种体验简直让人焦虑。

但如果是逐字输出呢?你能看到AI在"思考",能看到代码一行行生成,能提前发现方向是否正确——这种实时感让交互变得更加自然。

3.2 流式输出的技术实现

WebCode的流式输出实现非常优雅。核心是使用了C#的IAsyncEnumerable

public async IAsyncEnumerable<StreamOutputChunk> ExecuteStreamAsync(
    string sessionId,
    string toolId,
    string userPrompt,
    CancellationToken cancellationToken = default)
{
    // 获取工具配置和适配器
    var tool = GetTool(toolId);
    var adapter = _adapterFactory.GetAdapter(tool);
    
    // 启动进程
    var process = StartCliProcess(tool, userPrompt, sessionContext);
    
    // 流式读取输出
    await foreach (var chunk in ReadProcessOutputAsync(process, cancellationToken))
    {
        yield return chunk;
    }
}

IAsyncEnumerable是.NET中处理异步流的利器。它允许你在数据产生时就立即返回给调用者,而不需要等到所有数据都准备好。

在前端(Blazor组件)里,消费这个流也非常直观:

await foreach (var chunk in _cliExecutor.ExecuteStreamAsync(sessionId, toolId, prompt))
{
    // 实时更新UI
    _currentOutput += chunk.Content;
    StateHasChanged();  // 触发Blazor重新渲染
}

每读到一个chunk,就更新一次UI——这就是打字机效果的秘密。

3.3 输出解析:把混乱变成有序

AI CLI工具的输出通常是JSON格式的,但格式五花八门。Claude Code输出的是这样的:

{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"让我帮你..."}]}}

而Codex输出的是这样的:

{"event":"item.created","item":{"type":"message","content":[{"type":"text","text":"..."}]}}

WebCode的适配器会把这些不同格式统一解析成CliOutputEvent对象:

public class CliOutputEvent
{
    public string EventType { get; set; }      // 事件类型:message/tool_use/error等
    public string? SessionId { get; set; }     // 会话ID
    public string? Content { get; set; }       // 消息内容
    public bool IsError { get; set; }          // 是否是错误
    public CliCommandExecution? CommandExecution;  // 如果是命令执行
    public List<CliTodoItem>? TodoItems;       // 如果是待办列表
}

这样,无论底层是Claude还是Codex,上层UI都可以用统一的方式来展示——这就是抽象的力量


四、会话管理与工作区隔离

4.1 为什么需要会话隔离?

想象一下:小明在用你的WebCode写一个电商项目,小红在写一个博客系统。如果他们的文件都混在一起,那简直是灾难——小明的index.html可能会被小红的覆盖!

WebCode的解决方案是:每个会话拥有独立的工作区目录

private string GetOrCreateSessionWorkspace(string sessionId)
{
    lock (_workspaceLock)
    {
        // 如果已存在,直接返回
        if (_sessionWorkspaces.TryGetValue(sessionId, out var existingWorkspace))
        {
            return existingWorkspace;
        }

        // 创建新的会话工作目录
        var workspacePath = Path.Combine(workspaceRoot, sessionId);
        Directory.CreateDirectory(workspacePath);
        
        // 创建标记文件,记录创建时间
        File.WriteAllText(
            Path.Combine(workspacePath, ".workspace_info"), 
            $"Created: {DateTime.UtcNow}\nSessionId: {sessionId}"
        );
        
        return workspacePath;
    }
}

每个会话都有一个以sessionId命名的目录,里面存放该会话的所有文件。AI在执行命令时,工作目录就设置为这个会话目录——完美隔离。

4.2 过期工作区自动清理

工作区多了怎么办?总不能无限增长吧?

WebCode实现了一个后台清理服务,定期清理过期的工作区:

public void CleanupExpiredWorkspaces()
{
    var expirationTime = DateTime.UtcNow.AddHours(-_options.WorkspaceExpirationHours);
    var directories = Directory.GetDirectories(workspaceRoot);
    
    foreach (var dir in directories)
    {
        var markerFile = Path.Combine(dir, ".workspace_info");
        var lastAccessTime = File.GetLastWriteTimeUtc(markerFile);
        
        if (lastAccessTime < expirationTime)
        {
            Directory.Delete(dir, recursive: true);
            _logger.LogInformation("已清理过期工作区: {Path}", dir);
        }
    }
}

默认配置是24小时,超过24小时没有活动的工作区会被自动清理。当然,这个时间是可配置的。

4.3 会话恢复:跨请求的上下文保持

Claude Code和Codex都支持会话恢复——你可以在一个会话里多次对话,AI会记住之前的上下文。

WebCode完整支持这个特性。当AI返回会话ID时,系统会自动保存:

// 从输出中解析会话ID
var parsedThreadId = ParseCliThreadId(output, adapter);
if (!string.IsNullOrEmpty(parsedThreadId))
{
    SetCliThreadId(sessionId, parsedThreadId);
}

下次发送请求时,会话上下文会自动带上这个ID:

var sessionContext = new CliSessionContext
{
    SessionId = sessionId,
    CliThreadId = GetCliThreadId(sessionId),  // 自动获取之前保存的ID
    WorkingDirectory = sessionWorkspace
};

// 适配器会根据上下文构建恢复参数
// Claude: --resume <thread_id>
// Codex: resume <thread_id>

这意味着什么?你可以在手机上开始一个编程任务,晚上回家后在电脑上继续——只要会话没过期,AI就记得之前的对话!


五、移动端优化:真正的"随身编程"

5.1 响应式布局

WebCode在移动端的体验令人印象深刻。打开手机浏览器,你会发现界面自动适配成了上下布局:上面是聊天记录,下面是输入框。

这是通过Tailwind CSS的响应式类实现的:

<div class="flex flex-col lg:flex-row">
    <div class="w-full lg:w-1/2"><!-- 聊天区域 --></div>
    <div class="w-full lg:w-1/2"><!-- 预览区域 --></div>
</div>

lg:前缀表示只在大屏幕上生效,小屏幕上会使用默认的flex-col(垂直排列)。

5.2 触摸优化

移动端最恼人的体验是什么?按钮太小,总是点不中!

WebCode的所有可点击元素都遵循Apple的人机交互指南:最小44x44像素的触摸目标

.touch-target {
    min-height: 44px;
    min-width: 44px;
    padding: 12px 16px;
}

此外,还有按压反馈效果,让你知道自己确实点到了:

.touch-target:active {
    transform: scale(0.98);
    opacity: 0.9;
}

5.3 iOS Safari的100vh问题

如果你开发过移动端网页,一定被Safari的100vh问题折磨过——地址栏的出现和消失会导致布局跳动。

WebCode的解决方案:

.full-height {
    height: 100vh;
    height: 100dvh;  /* dynamic viewport height */
}

100dvh是CSS新增的单位,表示"动态视口高度",会随着地址栏的显示/隐藏自动调整。对于不支持的老浏览器,会降级到100vh


六、部署方案:Docker一键启动

6.1 30秒启动体验

WebCode的部署体验堪称完美。只需要三条命令:

git clone https://github.com/xuzeyu91/WebCode.git
cd WebCode
docker compose up -d

然后访问http://localhost:5000,完事。

首次访问时,系统会自动进入设置向导,引导你配置Claude/Codex的API Key等信息。不需要手动编辑任何配置文件——这才是现代软件应有的用户体验

6.2 Docker镜像的设计

看看Dockerfile,你会发现作者的用心:

# 多阶段构建
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
# ... 构建阶段

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
# 安装Node.js 20.x(Claude Code依赖)
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
    && apt-get install -y nodejs

# 安装Python 3(部分工具依赖)
RUN apt-get install -y python3 python3-pip

# 预装Claude Code CLI和Codex CLI
RUN npm install -g @anthropic-ai/claude-code @openai/codex-cli

# 非root用户运行(安全性)
RUN adduser --disabled-password --gecos "" webcode
USER webcode

多阶段构建、预装依赖、非root用户运行——这是一个生产级的Docker镜像。

6.3 数据持久化

docker-compose.yml配置了三个数据卷:

volumes:
  - ./data:/app/data          # 数据库和配置
  - ./workspaces:/app/workspaces  # 会话工作区
  - ./logs:/app/logs          # 日志文件

这意味着容器重启不会丢失任何数据——历史会话、用户设置、生成的代码文件都会保留。


七、安全特性:不只是能用,还要安全

7.1 命令白名单

虽然AI很智能,但我们不能完全信任它执行的命令。WebCode支持命令白名单:

{
  "CliTools": {
    "EnableCommandWhitelist": true,
    "CommandWhitelist": ["git", "npm", "node", "python"]
  }
}

只有白名单内的命令才能执行,有效防止AI"脑抽"执行rm -rf /这种危险操作。

7.2 并发限制

同时运行太多AI进程可能会拖垮服务器。WebCode使用信号量限制并发:

private readonly SemaphoreSlim _concurrencyLimiter;

public CliExecutorService(IOptions<CliToolsOption> options)
{
    _concurrencyLimiter = new SemaphoreSlim(options.Value.MaxConcurrentExecutions);
}

public async IAsyncEnumerable<StreamOutputChunk> ExecuteStreamAsync(...)
{
    await _concurrencyLimiter.WaitAsync(cancellationToken);
    try
    {
        // 执行命令
    }
    finally
    {
        _concurrencyLimiter.Release();
    }
}

默认最多同时执行3个AI请求,超出的会排队等待。

7.3 路径安全检查

文件操作时,系统会严格检查路径是否在工作区内,防止目录遍历攻击:

var normalizedWorkspace = Path.GetFullPath(workspacePath);
var normalizedFile = Path.GetFullPath(fullPath);

if (!normalizedFile.StartsWith(normalizedWorkspace))
{
    _logger.LogWarning("尝试访问工作区外的文件: {File}", relativePath);
    return null;
}

即使有人构造../../etc/passwd这样的恶意路径,也会被拦截。


八、实际应用场景

8.1 移动办公

这是WebCode最核心的使用场景。想象一下:

  • 早上通勤时,在地铁上用手机告诉Claude:"帮我优化一下昨天那个SQL查询"

  • 午休时间,在食堂用平板让Codex生成一组单元测试

  • 晚上躺沙发,用手机review AI生成的代码

编程不再是被电脑"绑架"的活动。

8.2 远程开发

很多公司的代码只能在内网开发机上编辑。有了WebCode,你可以把它部署在开发机上,然后通过VPN在任何地方访问。

这比VS Code Remote或JetBrains Gateway更轻量——你只需要一个浏览器。

8.3 教学演示

给学生讲解编程时,你可以一边说需求,一边让AI实时生成代码。学生能看到整个"从需求到代码"的过程,比看PPT有趣100倍。

8.4 快速原型

客户突然来电:"能不能做个XXX功能?"

以前你可能会说"我回去看看",现在你可以当场掏出手机,让AI生成一个原型,3分钟后给客户演示——这就是AI时代的生产力


九、未来展望:这只是开始

9.1 更多AI工具支持

目前WebCode完整支持Claude Code和Codex,GitHub Copilot CLI、Qwen、Gemini等都在适配中。

得益于优秀的适配器设计,添加新工具只需要实现一个接口——社区贡献者可以轻松参与进来。

9.2 协作功能

未来可能会加入:

  • 多人共享同一个会话

  • 实时看到其他人的输入和AI输出

  • 会话快照分享

想象一下,远程面试时,面试官和候选人同时看着AI生成的代码进行讨论——这是不是比共享屏幕更优雅?

9.3 插件生态

如果能开放适配器的动态加载,让用户上传自己的适配器来支持新的CLI工具,那将会是一个真正的平台。


十、写在最后

WebCode并不是什么革命性的创新——它只是把几个已有的东西巧妙地组合在一起:

  • Blazor Server的实时通信能力

  • CLI工具的命令行接口

  • 适配器模式的软件设计

  • Docker的容器化部署

但正是这种"把简单的事情做好"的态度,才让它成为一个真正好用的产品。

如果你也厌倦了只能坐在电脑前写代码,不妨试试WebCode。也许某天,你会和我一样,在拥挤的地铁里,对着手机屏幕上一行行生成的代码会心一笑。

编程,从此随身携带。


相关链接

  • 项目地址:https://github.com/xuzeyu91/WebCode

  • 在线体验:https://webcode.tree456.com/(账号:treechat,密码:treechat@123)


博主在参加2025博客之星活动

如果这篇文章对你有帮助,希望你能帮我投出宝贵的一票!

投票地址:https://www.csdn.net/blogstar2025/detail/070

每一票都是对我创作的巨大鼓励,感谢你的支持!

更多AIGC文章

RAG技术全解:从原理到实战的简明指南

更多VibeCoding文章

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐