news 2026/11/4 16:07:36

OpenChamber 文件接口 /api/fs 全解:从流式预览到命令执行,安全边界是怎么守住的

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenChamber 文件接口 /api/fs 全解:从流式预览到命令执行,安全边界是怎么守住的

OpenChamber 文件接口 /api/fs 全解:从流式预览到命令执行,安全边界是怎么守住的

【免费下载链接】openchamberAgentic Development Environment based on OpenCode AI agent项目地址: https://gitcode.com/gh_mirrors/op/openchamber

你在 OpenChamber 里点一次"打开文件"、传一张截图、在 Git 标签页选一个仓库,浏览器和桌面端都会向同一个 Web 服务器发请求——真正干活的是packages/web/server/lib/fs/模块注册的一组/api/fs/*接口。读文件、写文件、传文件、跑命令、搜仓库,它一个都跑不掉;而"你能碰哪些文件"这条线,也全系在这组接口上。

能力速览:这一组接口到底能干什么

  • 读:read读文本、raw流式读原始字节(支持 Range 分段)、stat查文件信息、list列目录(可按 gitignore 过滤)、git-dirs浅层找嵌套仓库。
  • 写:write原子写文本、upload流式传二进制、mkdir建目录、delete递归删、rename同根内移动。
  • 执行:exec在指定目录顺序跑 shell 命令,任务状态由服务端自维护,轮询取结果。
  • 搜索:文件名模糊搜索(search.js)和内容搜索find-in-files(ripgrep 驱动)。
  • 预览:/api/fs/preview把 HTML 页面放进沙箱里打开。
  • 辅助:reveal在系统文件管理器中定位路径、home返回家目录与托管聊天根。

所有写类操作都先过同一道工作区边界检查;所有 spawn 出去的子进程都有超时兜底。下面按你会真实用到的场景来讲。

场景一:看文件、放视频——读端点如何"喂饱"浏览器

能做什么:在 UI 里预览图片、PDF、音频视频,或读取文本内容。

怎么触发:三个读端点分工明确——

端点读什么返回
GET /api/fs/read文本(UTF-8)text/plain内容
GET /api/fs/raw原始字节按扩展名判 MIME,可分段
GET /api/fs/stat文件元信息{ path, isFile, size, mtimeMs }

背后怎么处理:raw是全模块最"会读"的端点。响应头里带着Accept-Ranges: bytes,浏览器播放器每次拖动进度条都会发一个Range: bytes=<offset>-请求;没有206 Partial Content应答,Chromium 和 WebKit 直接拒绝 seek,Safari 干脆拒绝播放。所以服务端按请求区间从磁盘开流(createReadStream({ start, end }))输出,几百 MB 的录音也不会整块读进服务器内存;区间不合法(比如起点已超过文件大小)则回416加Content-Range: bytes */<size>。

容易忽略的细节:

  • read有个"空读重试":stat说文件有内容、readFile却读到空串,说明另一个写入者刚截断还没写完,服务端会按 50/100/150ms 间隔重试最多 3 次,避免把半成品发给你。
  • HTML 预览走POST /api/fs/preview:服务端为这一页铸造一个短令牌放进 URL,页面里的相对资源(图片、样式、脚本)自动继承这个授权,页面整个跑在 opaque-origin 沙箱里;令牌空闲 10 分钟失效,每请求一个文件就续期一次。预览的页面本质是不可信内容,这个设计让"打开一个 HTML"不至于能顺手读走整个配置目录。

场景二:存文件、传文件——写端点为什么从不"直接落笔"

先说结论:任何写操作落盘前,都要过三道闸——工作区边界、规范化包含检查、原子提交。

怎么触发:write收 JSON{ path, content };upload收application/octet-stream请求体,目标用 query 参数指定。

背后怎么处理:

  • 边界白名单有三条:目标路径解析后落在活动项目目录内;或落在受管根(OpenChamber 配置根、托管聊天根,后者可用OPENCHAMBER_CHATS_DIR整体搬走);或落在该项目某个 git worktree 里。哪都不沾就 403。
  • 如果项目根本身是个符号链接,服务端还会拿客户端发来的"原始目录名"再比一次——否则用户看见的路径和规范化后的基座对不上,正常操作反而被误杀。
  • write内容没变就直接返回成功(幂等);变了就先写同目录临时文件再rename,读者永远不会看到截断到一半的空文件,临时文件写失败则清理后报错。
  • upload流式接收,累计字节一旦超上限(默认 100 MiB)立即 413 并删临时文件。

容易忽略的细节:

  • 上传目标已存在时:是目录 → 400;没声明overwrite=true→ 409(reason: "already-exists");显式声明覆盖才rename替换。新文件则用同目录硬链接提交——它不会替换检查之后才冒出来的同名目标,且临时文件已完全落盘,读者看不到半截上传。
  • 上传前父目录会被realpath规范化并做包含检查:借工作区里的符号链接把文件传到外面,行不通。
  • rename要求源和目标解析到同一个工作区根,目标已存在也拒绝(纯大小写改名除外);把文件夹移进自己肚子里直接 400。

场景三:找文件、搜内容——两层搜索各自怎么跑

能做什么:按文件名找(find-by-name,配合文件名索引秒回);按文件内容找(find-in-files)。

怎么触发:

curl "http://localhost:PORT/api/fs/find-in-files?query=resolveByteRange&limit=50"

背后怎么处理:文件名搜索由createFsSearchRuntime提供,被图标发现等非文件路由复用。它做并发受限的 BFS:每批最多并行读 5 个目录,跳过node_modules、.git、dist、build、coverage、logs等噪音目录,默认不进点号开头的隐藏项;开启respectGitignore时,每个目录对全部条目批量问一次:

git check-ignore -- <names>

命中的条目从候选里剔除。匹配用模糊评分:子串命中且位于文件名开头或分隔符之后加分最高,其余按字符顺序、相邻度、路径长度综合打分,分数不够直接丢弃;有查询词时会一直遍历到整棵树走完(上限 10000 个受检文件),而不是找到"看起来像"的就收手——否则浅层的弱匹配会先耗尽候选预算,深层的精确匹配永远轮不上。

内容搜索由 ripgrep 执行,可见范围跟文件树保持一致:.gitignore生效、二进制文件跳过、.git和node_modules永不参与;小写查询大小写不敏感,带大写字母则精确匹配。

容易忽略的细节:

  • 结果按行返回{ path, line, column, text, ranges },ranges是命中位置在行内的偏移,前端拿来高亮用。
  • 搜满limit(默认 200、上限 500)被杀掉,不是错误,响应里stopped: "limit";跑满 30 秒被杀同样不是错误,stopped: "deadline"。失败永远不等于空结果:正则不合法回 400invalid-pattern,服务器没有 ripgrep 回 501search-unavailable。
  • 你敲下一个新字符,上一个请求就断了,服务端直接把 ripgrep 进程杀掉——不给没人看的搜索浪费 CPU。

场景四:让服务端跑命令——exec 任务队列

怎么触发:POST /api/fs/exec,请求体:

{ "commands": ["git status", "git diff --stat"], "cwd": "/path/to/project" }

background字段目前被显式拒绝,命令永远前台顺序执行。

背后怎么处理:

  1. cwd必须过工作区检查且确实是目录,否则 403/400;
  2. shell 取$SHELL,Windows 回退cmd.exe(flag/c),其余平台回退/bin/sh(flag-c);
  3. 任务拿到一个jobId,状态由服务端自己维护的 Map 管理,走queued → running → done,每条命令独立收集 stdout/stderr,退出码 0 且没超时才算成功;
  4. 结果可以立即取(前台执行完才返回),也可以用GET /api/fs/exec/:jobId轮询,查不到 404。

容易忽略的细节:

  • 单条命令默认 5 分钟超时,到点SIGKILL,结果里带Command timed out after <ms>ms。
  • 任务只保留 30 分钟:每次建任务、轮询任务时顺手清理超龄条目,常驻服务器的内存不会随时间堆满。
  • exec 内藏一个 git 读缓存,但只有白名单命令能进:git rev-parse --absolute-git-dir、--git-common-dir、--show-toplevel的组合。任何带副作用的命令一律照跑、绝不缓存。缓存上限 500 条且总量不超 1 MiB,LRU 逐出,TTL 默认 30 秒;同一条命令的并发请求合并成一个在途 Promise,且只缓存成功结果——失败可能只是暂时的。这层缓存把"刷新页面后客户端对每个项目重复发的一批 rev-parse"几乎零成本地吸收掉了。

场景五:Git 标签页怎么知道哪里有仓库

"选择仓库"下拉框背后是GET /api/fs/git-dirs的受限遍历:

  • 广度优先,最多深入 3 层、最多访问 100 个目录——浅层仓库永远比深层目录先被看到;
  • node_modules、dist、build、.venv、target、.next直接跳过;
  • 目录里出现.git(目录、worktree 指针文件、符号链接都算)就构成仓库边界,停止下探,嵌套仓库不重复上报;根目录自己的.git例外——客户端只对"本身不是仓库或还没有提交"的根发这个请求;
  • 符号链接目录会跟进(用链接把散在各处的仓库聚成一个项目是常见做法),但每个真实目录只走一次,链接循环不会导致重复上报,仓库按链接给出的路径汇报。

设计取舍:为什么是这些"看起来多余"的动作

策略收在模块里,而不是组合根。routes.js 自己持有 exec 任务队列、git 读缓存、预览授权这些服务进程自维护的状态,组合根(index.js)只注入平台原语和回调。好处是边界策略改动不会波及其它路由,这个模块甚至能只靠 Node 内置模块加载。

"读"放宽、"写"收紧。读类端点带allowOutsideWorkspace=true就能读工作区外的文件——真正的闸门是服务端认证和操作系统权限。写类端点则一律先过白名单,落笔前再做一次规范化包含检查。两种拒绝也刻意分开:OS 层权限失败回 403 加reason: "os-permission",客户端看到这个标记知道可以打开原生文件夹选择器引导你授权;而策略性拒绝(Path is outside of active workspace)绝不用这个标记,因为换目录选择器也救不了它。

原子提交是贯穿全模块的动作。write 用临时文件加 rename,upload 用硬链接提交,preview 授权用续期而不是重发。目的只有一个:让并发读者、并发请求永远看不到中间态。

git 子进程的 stderr 纪律。⚠️ 目录列举和文件名搜索 spawngit check-ignore时,对 stderr 一律丢弃、只读 stdout。原因很朴素:Git 的诊断输出可能非常大,一旦建了一条没人读的 stderr 管道,缓冲区写满后子进程就永久阻塞,反复搜索会不断累积"活"的 Git 进程。仓库里专门有个测试模拟每个子进程往 stderr 灌 2 MiB 诊断、并发 12 个检查,断言全部完成、过滤仍生效、12 个子进程全被回收——任何改动若引入未读 stderr 管道,都会在这个测试的看门狗下超时。

所有内存态都有 TTL。exec 任务 30 分钟、git 读缓存 30 秒、预览授权空闲 10 分钟、工作区外文件授权 10 分钟。常驻进程里,"放进去就不管"的 Map 会随时间线性增长,所以每处都配了"创建时顺手清理"的钩子。

可配置项:这几个参数你真会去调

环境变量默认值作用
OPENCHAMBER_FS_UPLOAD_MAX_BYTES100 MiB上传体积上限,设为非正数回退默认
OPENCHAMBER_FS_EXEC_TIMEOUT_MS5 分钟单条 exec 命令超时,到点 SIGKILL
OPENCHAMBER_GIT_READ_CACHE_TTL_MS30 秒git rev-parse 读缓存 TTL,设0关闭
OPENCHAMBER_GIT_CHECK_IGNORE_TIMEOUT_MS2500 mscheck-ignore 子进程超时,超时按"无忽略"处理
OPENCHAMBER_CHATS_DIR<配置根>/chats托管聊天根目录,可整体搬迁

常见坑与排查

Q:上传返回 413?A:超限了。服务端对声明体积和实际流入字节都设了卡点,两者任一超上限都 413,临时文件自动清理。调大上限用OPENCHAMBER_FS_UPLOAD_MAX_BYTES(单位是字节)。

Q:exec 结果里有Command timed out after …?A:单条命令超过OPENCHAMBER_FS_EXEC_TIMEOUT_MS,子进程被 SIGKILL。长任务要么拆开,要么调大超时——注意超时结果里success是false,别当成功处理。

Q:403 到底是权限问题还是边界问题?A:看reason。带os-permission的是操作系统级拒绝,可以引导用户重新选文件夹;不带这个 reason 的 403/400 是工作区边界策略拒绝(典型文案Path is outside of active workspace),目标必须落在活动项目、受管聊天根或 worktree 之内。

Q:list结果里少了些目录?A:respectGitignore=true时会批量问git check-ignore,被.gitignore命中的条目被剔除;子进程 2.5 秒没跑完就 SIGKILL,按"没有忽略项"处理——所以列表照常返回,只是没过滤。这不是故障。

Q:find-in-files返回stopped: "limit"是不是搜失败了?A:不是。它搜满了 200 条上限(或跑满 30 秒)才停的,matches里是真实结果。想拿全要么收窄查询,要么知道limit上限是 500。

Q:/api/fs/preview打开的页面里图片加载失败?A:检查页面资源是否在授权范围内。预览授权锁定了页面所在工作区(或所在受管根)为读取边界,越界资源拿不到;令牌空闲 10 分钟也会失效,刷新页面重新铸造即可。

如果你要扩展

新增/api/fs/*端点就加在 routes.js 里,并同步更新 DOCUMENTATION.md:边界校验复用resolveWorkspacePathFromContext,OS 权限错误沿用reason: "os-permission",spawn 任何 git 子进程记得丢弃 stderr、只消费 stdout 并挂超时。

一句话收尾:OpenChamber 的/api/fs/*把"浏览器里的一切文件操作"收敛成一组带边界、带超时、带内存回收纪律的接口——理解它的五个场景,比背端点列表更有用。

【免费下载链接】openchamberAgentic Development Environment based on OpenCode AI agent项目地址: https://gitcode.com/gh_mirrors/op/openchamber

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 16:06:53

Windows 冗余驱动怎么找、怎么删:Driver Store Explorer 新手实操

Windows 冗余驱动怎么找、怎么删&#xff1a;Driver Store Explorer 新手实操 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer Windows 用了好几年&#xff0c;驱动仓库悄悄吃掉好几个 G…

作者头像 李华
网站建设 2026/11/4 15:44:01

如何快速部署Hermes Agent:5步搭建你的AI智能助手系统 [特殊字符]

如何快速部署Hermes Agent&#xff1a;5步搭建你的AI智能助手系统 &#x1f680; 你是否想过拥有一个能够自我学习、跨平台协作的AI助手&#xff1f;Hermes Agent正是这样一个开源AI代理工具&#xff0c;它能从经验中创建技能、在使用中不断改进&#xff0c;并为你提供无缝的智…

作者头像 李华
网站建设 2026/11/4 15:43:29

基于Springboot房地产销售管理系统【附源码+文档】

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/4 15:39:16

FaceFusion 换脸模型怎么下载:断网与离线部署的实操路径

FaceFusion 换脸模型怎么下载&#xff1a;断网与离线部署的实操路径 【免费下载链接】facefusion Industry leading face manipulation platform 项目地址: https://gitcode.com/GitHub_Trending/fa/facefusion FaceFusion 是面部交换、增强与编辑处理平台&#xff0c;O…

作者头像 李华
网站建设 2026/11/4 15:38:52

Liquid 模板前端框架集成实战:React 里安全渲染的五个要点

Liquid 模板前端框架集成实战&#xff1a;React 里安全渲染的五个要点 【免费下载链接】liquid Liquid markup language. Safe, customer facing template language for flexible web apps. 项目地址: https://gitcode.com/gh_mirrors/li/liquid 运营想自己改首页文案&…

作者头像 李华