拓冰建站拓冰建站
首页 / 资讯中心 / 正文

DeepSeek 开源的 Agent Harness 怎么给团队试?本地 dsh web 起在 127.0.0.1,用 cpolar 短时开放给异地同事看只读会话

DeepSeek 开源的 Agent Harness 怎么给团队试本地 dsh web 起在 127.0.0.1用 cpolar 短时开放给异地同事看只读会话上周想拉两个异地同事一起看一个 agent 会话跑成什么样结果卡在一个很普通的事上DeepSeek 官方开源的智能体框架 dsh它的 Web 界面默认只跑在127.0.0.1:3080。同事在另一个城市我总不能让人家为了看一眼会话轨迹去连我这台机器的内网。我要的其实很简单让同事在浏览器里看当前这个 harness 会话在干什么看完这条通道立刻关掉。不需要他执行任何操作不需要给他账号更不会把整台机器暴露出去。这篇就干这一件事。先把 dsh 的 Web 起在本机把它的鉴权和接口契约在命令行里验一遍确认它确实是有门的再用 cpolar 给3080开一条短时 HTTPS 隧道把带 token 的地址发给同事只读查看用完就关。下面所有命令和返回都是我在本机真实跑出来的。cpolar 免费套餐给的公网地址每次都不一样你按自己跑出来的地址替换就行。1 什么是 DeepSeek Harness这篇里它负责哪一段先把定位说清楚免得跟随便一个聊天 UI混起来。DeepSeek Harness命令名dsh是 DeepSeek AI 官方开源的 agent harnessMIT 协议TypeScript 编写仓库在github.com/deepseek-ai/deepseek-harness。它基于everything-is-a-plugin架构底层是 Cordis 组合框架。我确认版本的时候它还是 developer previewREADME 里用大写标了会有破坏性变更还专门有一份 SAFETY.md 要求先读。这一点必须摆在最前面它是开发预览版接口和命令行都会变。你今天能跑通的命令下个小版本就换了写法。这不是劝退是提醒你别把它当成稳定的生产组件。它在这篇里负责的角色很具体它是被看的那一方。我在这台机器上跑一个 harness 会话同事要看的就是这个会话的运行轨迹。它不是被穿透的目标本身而是我们要临时敞开一条观察缝的那扇窗。dsh 自带一个 Web 界面。官方的启动方式就一行npx deepseek-ai/dsh web默认它会监听127.0.0.1:3080然后自动帮你打开默认浏览器。README 里特意说明如果你是 SSH 登录上来的它只打印地址不打开浏览器因为本地转发地址归 SSH 客户端或编辑器管。要纯粹起服务、不弹浏览器加--no-open。还有个细节要提前交代因为后面会用到dsh 的 Web 界面不是裸奔的。它启动时会打印一条带 token 的地址没有 token 直接访问会吃 401而它的业务接口/api/namespace/method走的是 Typert API Gateway外层还有一道基于访问主机名的信任围栏。这两道门恰好就是只读查看能不能成立的根据。2 环境准备确认 Node 和 dsh 版本这篇不需要 Docker不需要双机甚至不需要浏览器就能把大部分东西验完。真正需要的就两样一个够新的 Node一个能联网的 npm 环境。先看 Nodenode -v # v26.8.1 npm -v # 11.19.0版本号能正常打印就行。dsh 走的是npx所以不用全局装跑的时候它会自己拉。确认一下要跑的是哪个 dsh 版本npx --yes deepseek-ai/dshlatest --version # 0.1.5-rc.2这里要留意一下版本号。我跑出来是0.1.5-rc.2带rc后缀正好对应它 README 里说的 developer preview。如果你的输出是个更新的版本后面的命令大概率照样能用但细节比如某个参数名以你本地dsh web --help的输出为准。别把我的版本号当成硬标准抄进脚本里。顺便把dsh web的参数看一眼这一步很多人会跳过但它直接决定了后面隧道能不能用npx --yes deepseek-ai/dshlatest web --help输出里我关注到两项--host host bind host --no-open do not open the Web UI in the default browser --port port listen port; pass 0 to let the OS pick a free one --trusted-host authority... extra authority the /api browser-trust fence accepts (host or host:port; repeatable)--host和--port是常规的绑定项先记住怎么用。--trusted-host这一项很关键——它描述里写着额外的 authority/api的 browser-trust fence 会接受。也就是说dsh 的接口层是按访问主机名放行的。记住这句话第 6 节会用它解释一个现象。环境这块就这些没有别的依赖。3 用 headless 方式把 dsh web 起起来服务启动很简单但我不想让它在服务器上还自动弹一个浏览器很多时候根本弹不出来还会卡住所以显式加--no-opennpx --yes deepseek-ai/dshlatest web --no-open第一次跑会看到一堆 npm 的 deprecation 警告比如npm warn deprecated node-domexception1.0.0这个不用管。真正重要的是最后那行dsh web: http://127.0.0.1:3080/?tokenSeBJBxRuQNoaI2CGyZZFQckZHrinYtZx实际 token 更长仅为展示形态截断这一行就是本文最重要的东西之一。它同时告诉你三件事服务绑在127.0.0.1端口是3080以及一个进入 Web 界面的 token。敲重点这条 URL 里的 token 是入口凭证等同于一把钥匙。你把它发给谁谁就能进这个 Web 界面。后面开放给同事看的时候我们要发的是这条带 token 的地址——这也是为什么整个过程必须短时、用完即关。想后台跑、方便贴日志的话可以这样nohup npx --yes deepseek-ai/dshlatest web --no-open dsh-web-start.log 21 然后确认端口真的在监听lsof -iTCP -sTCP:LISTEN -P -n | grep 3080 # node 55421 admin 14u IPv4 0x… 0t0 TCP 127.0.0.1:3080 (LISTEN)看到127.0.0.1:3080 (LISTEN)就对了。注意这里的关键词是127.0.0.1不是0.0.0.0也不是*。这句话分量很重默认情况下这个界面只能从本机访问你同网段的同事、隔壁房间的另一台电脑都连不上。这正是后面需要 cpolar 的原因。想确认这一点可以拿本机的局域网地址试一下ipconfig getifaddr en0 # 192.168.50.161 curl -s -m 3 http://192.168.50.161:3080/ # curl: (7) Failed to connect连接直接失败。服务只认回环地址局域网都进不来更别说公网。如果你希望同事在同一局域网里直接看可以改用--host 0.0.0.0让它监听所有网卡。但那样等于对整栋楼的网络敞开本文不推荐。我们的目标是一条可控的、用完就关的通道不是一个长期挂着的监听端口。4 先验鉴权没 token 到底能不能进服务起来了接下来这一步不是为了测试而测试——我要先确认这个界面确实有门否则后面拿它做只读分享就毫无安全感。先不带任何凭证访问根路径curl -s -i http://127.0.0.1:3080/返回HTTP/1.1 401 Unauthorized cache-control: no-store content-type: text/plain; charsetutf-8 dsh web authentication required; reopen the URL printed by dsh web.401加上这句提示说明没有 token 进不去。顺手再试一个接口路径curl -s -i http://127.0.0.1:3080/api/health # HTTP/1.1 401 Unauthorized # unauthorized也是 401。现在带上刚才那行 token 里的值curl -s -i http://127.0.0.1:3080/?tokenSeBJBxRuQNoaI2CGyZZFQckZHrinYtZx返回变化了HTTP/1.1 303 See Other location: / set-cookie: dsh-auth-…v1.…; Max-Age2592000; Path/; HttpOnly; SameSiteStrict303 是换乘它不直接把页面给你而是把你重定向到/同时给你发一个dsh-auth-…的 Cookie。后面再带着这个 Cookie 访问就是正常页面了curl -s -c cookies.txt -o /dev/null http://127.0.0.1:3080/?tokenSeBJB… curl -s -b cookies.txt -i http://127.0.0.1:3080/ # HTTP/1.1 200 OK # content-type: text/html; charsetutf-8200到手链路通了。这里有个细节值得单独说那个dsh-authCookie 上带着HttpOnly; SameSiteStrict。HttpOnly意味着 JS 读不到它SameSiteStrict意味着它不会被别的站点带上来。这两点合起来让这个会话凭证很难被跨站手段偷走。顺手试个错的 tokencurl -s -o /dev/null -w %{http_code}\n http://127.0.0.1:3080/?tokendeadbeef # 401token 是校验过的瞎编一个进不去。到这一步我心里就有底了这扇窗是有锁的接下来才敢谈把它短暂地开给同事看。5 再验接口契约/api上到底拦不拦得住页面鉴权验完了还有一层要确认dsh 的业务接口。它的文档里写得很清楚远程调用走POST /api/namespace/method请求信封是一个client-request结构里面要有rpcId、method、payload。接口本身还会用描述符校验参数。这一段很值得较真因为它决定了同事只能看、不能动这个结论硬不硬。先看没带 Cookie 的时候接口是什么反应curl -s -i -X POST http://127.0.0.1:3080/api/goals/create \ -H content-type: application/json -d {args:{}} # HTTP/1.1 401 Unauthorized # unauthorized挡住。带上 Cookie但故意发一个结构不对的请求少了type、rpcId、method、payloadcurl -s -b cookies.txt -X POST http://127.0.0.1:3080/api/goals/create \ -H content-type: application/json \ -d {args:{objective:demo}}返回一个结构化错误{type:server-response,rpcId:invalid-request, result:{ok:false, error:{code:gateway/bad-request,message:invalid client-request message, details:{issues:[ {code:invalid_value,values:[client-request],path:[type]}, {expected:string,code:invalid_type,path:[rpcId]}, {expected:string,code:invalid_type,path:[method]}, {code:invalid_type,expected:nonoptional,path:[payload]}]}}}}再看错误的写法信封对了但args里的字段跟接口描述符对不上curl -s -b cookies.txt -X POST http://127.0.0.1:3080/api/goals/create \ -H content-type: application/json \ -d {type:client-request,rpcId:r1,method:goals/create, payload:{args:{objective:demo}}}{type:server-response,rpcId:r1, result:{ok:false, error:{code:gateway/arguments-invalid, message:typert gateway: goals/create: args fields do not match the descriptor: missing \agentId\, \request\; unexpected \objective\, details:{endpoint:goals/create}}}}两条错误信息都很明确地指向了原因gateway/bad-request是信封级别不合法gateway/arguments-invalid是参数描述符不匹配——它甚至告诉你缺了agentId、request多了个不认识的objective。这一步的价值在哪它证明了/api不是随便一个 HTTP POST 就能把事办了信封结构要合规、参数要匹配描述符、还得先过鉴权。三道关叠在一起接口不是裸的。我在本机用脚本把这几条都做成了可断言的检查跑完PASS10 FAIL0包括无 token 401、带 token 303、带 Cookie 200、错误 token 401、未知 namespace 404。顺带提一句404访问一个不存在的接口路径curl -s -o /dev/null -w %{http_code}\n -b cookies.txt \ -X POST http://127.0.0.1:3080/api/nosuch/method \ -H content-type: application/json \ -d {type:client-request,rpcId:r2,method:nosuch/method,payload:{args:{}}} # 404只有声明过的接口才被路由没声明的直接 404。这说明它的接口面是白名单式的不是什么都能试。到这里本机侧的验证就齐了页面上锁、接口上锁、参数校验严格。接下来才是这篇真正要解决的事——怎么让异地同事看到。6 用 cpolar 给 3080 开一条短时隧道本地127.0.0.1:3080同事是够不着的这就需要一条从公网连进来的临时通道。cpolar 在这里负责的就一件事把本机3080临时映射成一个可访问的 HTTPS 地址它不改 dsh 的任何行为也不绕过 dsh 的鉴权。先确认 cpolar 装好了cpolar version # cpolar version 3.3.18有版本号输出就行。没有的话去官网下载页https://www.cpolar.com/download按平台装macOS 走 Homebrewbrew tap probezy/core brew install cpolar登录态也得先有可以在 Web UIhttp://127.0.0.1:9200登录后自动写入 token或者命令行手动绑定cpolar authtoken 你的authtoken。我这边本机服务已在跑curl -s http://127.0.0.1:9200能返回页面直接开隧道。6.1 给 3080 开 HTTP 隧道cpolar http -logstdout 3080这里我特意加了-logstdout。原因是不加的话隧道信息不会稳定出现在你能抓到的输出里你就没法确认地址到底出来没有。加上它stdout 里会清清楚楚打印出来。关键几行长这样levelinfo msg[:client] Authenticated with switch server, client id: … levelinfo msg[:tunnel server module] Tunnel established at http://2ee30571.r3.nas.cpolar.cn levelinfo msg[:tunnel server module] Tunnel established at https://2ee30571.r3.nas.cpolar.cn看到Tunnel established at才算成功。在没看到这一行、或者 9200 面板里隧道还没显示 online 之前不要跟同事说好了。光看命令没报错、或者只看到进程还在跑都不算数。这次拿到的是https://2ee30571.r3.nas.cpolar.cnhttp 和 https 两个地址都有发给同事当然用 https 那个。地址每次都不一样别抄死。免费套餐给的是随机临时地址24 小时内会变。我前几次跑出来的是完全不同的域名前缀。这恰好也符合短时演示的用法地址本来就该一次性。6.2 从公网验证只读访问现在从公网地址访问验证同事那边会看到什么。先不带 tokencurl -s -o /dev/null -w %{http_code}\n https://2ee30571.r3.nas.cpolar.cn/ # 401依然是 401。dsh 的鉴权在隧道背后照样生效cpolar 没有帮人翻墙进门。带上 token 的那条完整地址https://2ee30571.r3.nas.cpolar.cn/?token…访问curl -s -i https://2ee30571.r3.nas.cpolar.cn/?tokenSeBJB…HTTP/2 303 location: / set-cookie: dsh-auth-…v1.…; Max-Age2592000; Path/; HttpOnly; SameSiteStrict还是 303 换 Cookie 那一套只不过这次的 Cookie 里编码的authority变成了公网域名。再带着这个 Cookie 请求curl -s -b pub_cookies.txt -o /dev/null -w %{http_code}\n https://2ee30571.r3.nas.cpolar.cn/ # 200页面出来了标题是DeepSeek Harness。这就是同事打开浏览器会看到的输入带 token 的地址302/303 一跳页面正常显示。6.3 一个意外收获/api在公网侧天然打不开验证只读的时候我顺手试了下接口结果很有意思——公网侧访问/api一律被挡curl -s -o /dev/null -w %{http_code}\n -b pub_cookies.txt \ https://2ee30571.r3.nas.cpolar.cn/api/health # 403 curl -s -o /dev/null -w %{http_code}\n -b pub_cookies.txt \ -X POST https://2ee30571.r3.nas.cpolar.cn/api/goals/create \ -H content-type: application/json -d {args:{}} # 403注意不是 401是 403。本地访问同样的路径是 200/401公网侧却统一 403——这两个数不一样说明它的拒绝发生在更早的一层。我还试了把 token 直接拼在 POST 的查询参数里、带Origin/Referer/Authorization头全都是 403。回过头看第 2 节那次dsh web --help答案就在里面--trusted-host authority... extra authority the /api browser-trust fence acceptsdsh 的/api上有一道基于访问主机名的信任围栏browser-trust fence只有被信任的 authority 才放行。我做了个对照实验用127.0.0.1:3099起 dsh并显式加上--trusted-host example.com然后伪造不同的 Host 头发请求——Host 头POST /api/goals/create结果127.0.0.1:3099默认信任200进入接口层example.com手动加进信任列表unauthorized越过围栏进到传输层鉴权evil.com没被信任forbidden对照就清楚了公网域名2ee30571.r3.nas.cpolar.cn不在 dsh 的信任列表里所以/api一律 403。这个发现让只读这个说法终于有了硬支撑。我之前想的是跟同事说好别乱点现在变成了想点也没有接口可用。同事拿到的是一扇能看到页面、但摸不到接口的窗。如果有人确实需要在隧道域名上调/api那就得显式加--trusted-host 公网域名那是主动放宽属于另一个决定本文不做。7 把只读地址发给同事用完就关到这一步可以交付了。发给同事的就一条https://2ee30571.r3.nas.cpolar.cn/?token完整token同事浏览器一开就能看到 harness 界面能看会话轨迹/api那边打不开。看完了呢关掉隧道。pkill -f cpolar http然后立刻确认地址失效。这里要等几秒让进程真的退干净再测curl -s -o /dev/null -w %{http_code}\n https://2ee30571.r3.nas.cpolar.cn/ # 404公网地址变成 404 了。同时本地服务毫无影响curl -s -o /dev/null -w %{http_code}\n http://127.0.0.1:3080/ # 401 本地还在只是没带 token curl -s -b cookies.txt -o /dev/null -w %{http_code}\n http://127.0.0.1:3080/ # 200公网 404、本机 200——这才是我要的收尾。隧道一停外面那条路就没了本地该怎么跑还怎么跑。别只 kill 完就完事一定要回测一下。我第一次停隧道后立刻测还是 401因为进程没退干净等几秒再测才是 404。我关过命令了和地址真的不可达了是两回事要拿结果说话。8 安全边界我实际控制了什么这一节不能省。给别人开一条公网通道光说没事的没用得把实际做的边界一条条列出来。第一只开一条隧道只映射3080。本机跑了别的服务、别的端口都不在公网面上。cpolar 这次只把3080顶出去宿主机的其它端口一个都没暴露。第二只监听回环不监听局域网。前面验过服务绑的是127.0.0.1局域网地址192.168.50.161连都连不上。公网入口只有 cpolar 这一条其余方向全是关的。第三dsh 自己的鉴权全程生效隧道不绕过它。公网无 token 访问是 401不是 200。token 是 dsh 签的、Cookie 是 dsh 发的cpolar 只是搬运字节。第四业务接口在公网侧被 dsh 的信任围栏挡成 403。也就是同事能看页面、摸不到/api。这一条是 dsh 自身机制送的不是我加的规则。第五带 token 的地址当敏感凭证对待。它等同于进门的钥匙只发给需要的同事看完就作废隧道一关地址就没了。不要把它贴到群里、写进文档、或者当成长期链接存起来。第六用完即关并回测确认不可达。见上一节404 是唯一能接受的收尾状态。第七全程只用一个示例会话不接入真实生产数据。给同事看的是 harness 本身在干什么不是公司任何真实业务或密钥。这条尤其重要——别为了演示效果顺手把真实环境接进来。把这七条合起来只读查看才站得住看得到、动不了、用一时、不开长。9 验收与清理整套跑完我把本机侧的断言做成了一个可重跑的脚本结果全部通过 dsh 本地 HTTP 验证 dsh version 0.1.5-rc.2 token length 43 [PASS] 端口 3080 处于 LISTEN - 1 [PASS] GET / 无 token - 401 [PASS] GET /api/health 无 token - 401 [PASS] GET /?token 换会话 - 303 [PASS] GET / 带 Cookie - 200 [PASS] GET / 错误 token - 401 [PASS] POST /api/goals/create 错误 envelope - gateway/bad-request [PASS] POST /api/goals/create 正确 envelope - gateway/arguments-invalid [PASS] POST /api/nosuch/method - 404 [PASS] 局域网地址 192.168.50.161 访问 - 000 结果PASS10 FAIL0 里面这几条正是这篇的核心入口有 token401/303/200、接口有契约bad-request / arguments-invalid、监听不外扩局域网000。收尾动作也不复杂# 关隧道 pkill -f cpolar http # 关 dsh pkill -f dsh web # 确认端口释放 lsof -iTCP:3080 -sTCP:LISTEN -P -n清理完验证一下公网地址返回 404、本地端口不再监听就干净了。有几个点我这次没实测如实说明固定二级子域名需要基础套餐或以上本文只验证了免费套餐的随机临时地址没验证保留子域名的写法异地同事从另一个网络的实际浏览器访问我这边是同一出口网络没做异地双网络测试页面内的会话交互式查看我只验了能 200 打开、标题正确没在页面里做点击操作Windows / Linux 上的等价复现也是这次没做的。这些你落地时按自己的环境补上。10 总结到这里让异地同事看一眼 harness 会话这件事算是干净利落地闭环了dsh web headless 起在本机127.0.0.1:3080鉴权和接口契约都在命令行里验过cpolar 给它开一条短时 HTTPS 隧道同事浏览器打开带 token 的地址看页面/api那边被 dsh 自己的信任围栏挡在外面看完隧道一关、地址即失效。关键步骤浓缩成三步起npx --yes deepseek-ai/dshlatest web --no-open确认127.0.0.1:3080 (LISTEN)记下带 token 的地址。验无 token 401、带 token 303 换 Cookie、带 Cookie 200/api的错误信封分别是gateway/bad-request和gateway/arguments-invalid。开cpolar http -logstdout 3080拿到临时 HTTPS 地址只发带 token 的那条给同事用完pkill回测公网变 404。这套做法最舒服的地方在于职责分得开dsh 管鉴权和接口放行cpolar 只负责给一条临时通路两边互不越界。安全上也不用赌同事的自觉——页面能看、接口 403这个边界是机制给的不是约定给的。等哪天需要长期给团队用再考虑升级 cpolar 套餐换固定二级子域名或者显式配--trusted-host放开某个域名扩展路径都摆在那儿但那都是另一个阶段的决定了。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门