内置浏览器
智能体整天在给你链接:刚启动的开发服务器、引用的文档页、刚开的 pull request。在桌面应用里,这些页面直接在 Codeg 内部打开,作为文件面板里的标签页挨着你的代码,而不是把你甩到另一个应用去。页面由真正的浏览器引擎渲染——macOS 上和 Safari 同一个 WebKit,Windows 上是 WebView2,Linux 上是 WebKitGTK——有自己的 cookie、自己的历史和自己的地址栏。引擎本身没有任何东西是模拟的:就连平板和手机视图改变的,也只是页面排版所用的尺寸。
也正因为页面在 app 里,它同时是一块你和智能体共用的界面:你可以把整页、页面上的某个元素、一张截图或控制台里的报错递进对话,也可以把某个页面共享给智能体去读、去操作,而它做的每一步都写在那个标签页上,你看得见。
它的设置界面叫「浏览器操作」
下文提到的每一项都在 设置 → 浏览器操作 里 —— 到 0.33.0 之前它叫浏览器,自 0.31.2 起是一个独立入口。在那之前,它是 设置 → 常规 底部折叠着的一个区块,旧说明会把你指到那儿。
桌面优先
在浏览器会话(codeg-server)里没有可嵌入的浏览器引擎:网页链接都在你正在用的浏览器里开新标签页,这里描述的设置项也只有三项适用——站点规则(用于阻止)、本机服务和终端链接菜单。唯一的例外是跑在 Codeg 主机上的开发服务器——智能体打印出来的 http://localhost:3000——它会经由端口桥作为工作台里的标签页打开。
连接到远程工作区的桌面窗口自 0.32.2 起也有内置浏览器,并通过它打开远程主机自己的地址。→ 在远程工作区窗口中
链接在哪里打开
app 里的每一个 http(s) 链接都经过同一个决定,在你点击的那一刻做出:
- 该主机的站点规则直接胜出(见下文站点规则)。写着阻止的规则会拦下这次点击并给出提示。
- 否则按链接来源的默认设置——对话消息、工具结果、终端、编辑器和通知各有自己的默认值,初始都是内置浏览器。在设置 → 浏览器操作 → 链接在哪里打开里逐项修改。
- ⌘ 点击(macOS)或 Ctrl 点击(Windows、Linux)让链接换一种方式打开,只此一次:默认是内置时用系统浏览器,默认是系统浏览器时开内置标签页。
链接上的右键菜单明确给出同样三个选择——在内置浏览器打开、在系统浏览器打开、复制链接——而非网页的链接保持原有行为:文件路径打开文件,mailto: 和 tel: 交给操作系统,未知的 scheme 会被拒绝而不是交给任何程序。
链接第一次在内置浏览器里打开时,Codeg 会提示一次,toast 里带一个始终使用系统浏览器按钮,留给更愿意维持原状的人。
终端还多一个选项:终端链接菜单(默认关)让普通点击先在指针处弹出一个小菜单——内置浏览器、系统浏览器、复制链接——而不是立即打开。⌘/Ctrl 点击仍会直接打开链接。
浏览器标签页里有什么
浏览器标签页只有一条小工具栏,其余全是页面:
- 地址栏。输入地址回车即可。裸主机名会补上
https://(回环与内网地址补http://);没有搜索兜底——地址栏就是地址栏。 - 后退、前进、刷新 / 停止、复制链接、在系统浏览器打开。
- 设备——紧挨在地址栏右边的按钮,把页面按平板、手机或你输入的尺寸来排版。→ 以平板或手机查看页面
- ⌘W 关闭的是标签页,不是窗口。在 macOS 上,焦点位于页面内部时——浏览器标签页、HTML 文档视图、HTML 预览——按 ⌘W 会关闭那个标签页,和在工作区其他任何地方一样。到 0.32.3 之前,这个按键会落到菜单的 Close Window 上,把整个窗口一起带走。
- 页内查找:⌘F / Ctrl-F——Enter 与 Shift+Enter 前后步进,Escape 关闭。搜索是引擎自己做的,页面既看不到也干扰不了。(没有「第 n / 共 m 个」计数:WebKit 只回答这一步找到了没有。)
- 页面内的链接。⌘/Ctrl 点击在当前标签页旁边开一个后台标签页。你点了什么之后页面开的窗口——OAuth 弹窗、
target="_blank"链接——会成为紧邻其开启者的标签页,并像在浏览器里一样保留window.opener与 referrer。没人要的弹窗(定时器、加载时的脚本)会被拦下并在工具栏下方的条里说明,带一个仍然打开按钮,把该地址作为普通标签页打开。自 0.31.1 起,办完事自己关闭的弹窗——Google 登录就是这么收尾的——会把自己的标签页一起带走,不再留下一个空的 Sign In 标签页。只有页面自己开出来的标签页才能这样自我关闭,普通标签页不行。 - 空白标签页是我们的,不是引擎的。自 0.31.1 起,空的浏览器标签页用应用自己的配色绘制——深色模式不再开出一张白纸,切换主题会把已经开着的空白标签页一起重绘,应用内的 toast 也能正常显示在它上面。
- 下载。页面提供的文件会以消毒后的文件名落到你的下载文件夹;已有文件绝不会被覆盖(你会得到
名字 (1).扩展名),也绝不会自动打开任何东西。下载条只提供在文件夹中显示。 - 打不开的页面。找不到主机、证书无效、服务器拒绝连接——各有自己的文案,措辞来自引擎,在引擎放弃的那一刻出现,带重试和在系统浏览器中打开按钮。被站点规则阻止的页面显示阻止页,只有重试(并指明规则所在)。
- 被拒的跳转。页面自己想去标签页不允许去的地方——
mailto:、自定义 scheme、被阻止的主机——页面停在原地,工具栏下方的条说明原因。对mailto:和tel:,条上提供用系统应用打开;其它 scheme 保持拒绝。
菜单、对话框和抽屉打开在页面之上:弹层打开期间,页面显示它的最后一帧,弹层一关即恢复实时。
有三样浏览器有、而这个首发版本还没有的东西:网站图标、文件下载过程中的进度(下载只在完成时汇报),以及在你切过去之前预加载标签页。
以平板或手机查看页面
从 0.34.0 起,一个标签页可以按某种设备的尺寸来排版页面,这样不用调整任何东西的大小就能检查响应式布局。紧挨在地址栏右边的设备按钮——提示文字是设备:桌面——会打开一个标题为设备类型的菜单:
| 设备 | 页面尺寸 |
|---|---|
| 桌面 | 铺满面板——页面像一直以来那样占满整个标签页。默认值 |
| 平板 | 768 × 1024 |
| 手机 | 390 × 844 |
| 自定义 | 你输入的宽度和高度 |
设备标签页会把页面放在面板中央的一个边框里,尺寸写在它上方。面板比设备小时,边框会缩小,页面也按同样的比例缩放——上方那一行会写出比例,比如 · 62%——所以页面仍然按设备的宽度排版,而不是按面板的宽度重新排。引擎不会把页面缩小到 25% 以下,所以当设备尺寸超过面板尺寸的四倍时,页面会按比要求更小的尺寸排版,一行琥珀色的提示会写明它实际拿到的尺寸:面板太小:按 1872 × 1052 排版。预设都是竖屏;要看横过来的手机,用自定义设成 844 × 390。
自定义会把边框上方的尺寸变成两个输入框,光标已经在宽度里了。Enter 或点到别处会保留你输入的值,Escape 恢复原来的值,↑ / ↓ 每次加减 1——按住 Shift 则是 10。尺寸以整数 CSS 像素计,范围是 100 到 8192;超出范围的值会被拉回范围之内。一个标签页切到自定义时,从你上一次输入的尺寸开始——还没输入过时是 1280 × 800——此后每个标签页各自保留自己的尺寸。
改变的只有尺寸
设备视图只改变页面的视口,别的都不变。页面保留引擎自己的 user agent 和鼠标输入——不会模拟触摸——所以按浏览器类型挑选手机版的网站,给你的仍是桌面版。跟随宽度变化的东西,比如媒体查询和响应式网格,看到的才是设备的尺寸。
- 每个标签页都记得自己的设备——在后台被卸载时、重启之后,以及用 ⇧⌘T 重新打开时都一样;只是如果同一个配置文件里已经有另一个标签页开着这个地址,被带到前面的会是那一个。新标签页一律从桌面开始,不管是谁打开的:你、一个链接、一个弹窗,还是一个智能体。没有任何浏览器工具能改变标签页的设备。
- 在面板里,开着设备视图时页面的缩放由 Codeg 掌管。 每次适配边框时它都会重新应用缩放,所以你自己在页面里做的缩放——Windows 上的 Ctrl+滚轮——会在下一次适配时被撤销;切回桌面时,页面会被恢复到 100% 一次,之后缩放又交还给你。
- 独立窗口里的标签页——也就是标签页承载方式设为独立窗口时;到 0.35.2 之前,Linux 上的每个标签页都是这样——不加边框,而是直接调整窗口本身的大小,让页面区域与设备的原始尺寸一致;切回桌面时,窗口会恢复原来的大小。尺寸,或者自定义的两个输入框,会移到面板里该标签页的卡片上,也就是写着该页面显示在独立窗口中。的那一张。窗口里页面的缩放保持你设定的样子,所以在意精确尺寸时,请让它保持在 100%。
- 在浏览器会话里,端口桥标签页也有同一个按钮,位于重新加载之前。在那里边框是整体缩小,而不是缩放页面,所以页面始终按完整的设备尺寸排版,面板太小那一行也永远不会出现。
终端里起的服务
在 Codeg 终端里启动一个开发服务器——或者让智能体去启动——Codeg 会读它打印出来的地址。设置 → 浏览器操作 → 本机服务决定接下来发生什么:
- 通知我(默认)——一条 toast 写着有服务正在 … 运行,带一个打开按钮。
- 直接打开标签页——页面在后台标签页里打开,你正在看的东西继续占着这一栏。
- 什么都不做——完全不打扰。
只有本机地址算数,而且要等到那个地址上真的有东西在监听:有些框架在端口绑定之前就把地址打印出来,那样开出来的标签页只会加载失败。
无论这个设置怎么选,标签条末尾的 + 都会列出此刻正在运行的本机服务,旁边是开空白页的浏览器标签页——所以被你顺手关掉的、或者在你想起它之前就起好的服务,永远只隔着一个菜单。
HTML 文件:文档视图
在文件面板里打开的 .html 文件——智能体写的报告、生成的图表、静态站点的 index.html——同样由内置浏览器显示,以文档视图直接从文件所在文件夹提供内容,而不经过任何 web 服务器。它是一个有真实地址的真实页面,所以相对链接、图片、样式表以及页面之间的互相链接都能用;它不被允许的,是离开那个文件夹。
- 默认安全模式。文档以无脚本、无网络的方式渲染:只有它自己文件夹里的标记、样式、图片和字体,别无其它。这和内联预览一直以来的姿态相同。
- 启用脚本(视图头部的按钮)把这一个文件切换到动态模式,本次会话内有效:它的脚本可以运行,并能读取文件夹里的其它文件(
fetch('data.json')可用)。它们依然不能访问网络、不能开窗口、不能下载任何东西。这个文件夹是文件所在的工作区文件夹,所以在工作区内../shared/app.js也能用;不在任何工作区文件夹里的文件被限制在它自己的目录中。 - 你批准的才会运行。动态模式会记住你启用它的时刻。之后只要文档用到的任何文件发生变化——智能体重写了脚本、构建替换了资源——视图就会自行退回安全模式、重新加载,并说明是哪个文件变了,附一个再次启用按钮。批准之后才改动的内容,绝不会在脚本开启的状态下被提供。
- 指向外部的链接。指向网页的链接不会在视图内跟随;一条提示条提供打开链接,按 app 里任何链接的打开方式打开(你的默认设置与站点规则都适用)。
mailto:和tel:得到用系统应用打开。文档里无法下载。 - 编辑。视图显示的是已保存的文件;编辑器里未保存的修改在保存之前不会反映,已保存的文件会自动重新加载。
- 内联预览还在。视图的 ⋯ 菜单里有针对这个文件的使用内联预览;设置 → 浏览器操作 → HTML 文件预览可以对所有文件关闭文档视图(网页版始终使用内联预览)。
文档视图在 macOS 与 Windows 上可用。Linux 继续使用内联预览:自 0.35.2 起它的标签页已经嵌入工作区,但文档视图赖以提供内容的那个私有地址 scheme,在 Linux 上还无法为这些标签页注册。文档视图永远不会共享给智能体:它根本不会被列给智能体,也无法被授权,无论平台给了它什么样的地址。
把页面递给对话
浏览器标签页同时也是一条通往对话的路。地址栏右侧的发给对话控件会打开一个以把这个页面发给对话为标题的菜单,提供四种方式:
- 选择一个元素…——指针变成拾取器;在页面上点中什么,发过去的就是那个元素。Escape 停止拾取。
- 发送一张截图——标签页此刻显示的画面。
- 标注一张截图…——同样是一张截图,但先在一个编辑器里打开,你可以在上面画带编号的方框和箭头,并可撤销和清除。加入对话会把标注后的图片连同每个标注在页面上的位置一起发过去,用的是页面自己的坐标——于是你消息里的「方框 2」指向的是一个真实存在的东西。0.32.2 新增。
- 发送控制台报错——页面打印了什么、什么出了错。什么都没出错时,这一项会直说并且无法点选,而不是塞给你一个空列表。
每一样都带着它来自的页面,落进你当前打开的那个对话的输入框,成为一枚胶囊——页面元素、页面截图、带标注的截图、3 条控制台报错——发送之前你仍然可以把它取下。发出去的消息会保留这枚胶囊,下方注明它来自哪个站点,实时、在历史记录里、在另一个客户端上都一样。没有打开的对话时,控件会说明它缺的是什么。
标注一张截图
编辑器打开时显示这张截图,并默认选中方框;在图上拖动即可绘制,每个标注依次获得下一个编号。箭头则改为画一个指示箭头。标注是红色的,带一圈白色描边;没有颜色、文字或自由手绘工具。⌘/Ctrl+Z 撤销,清除把全部标注一并抹掉(这一步本身也能撤销),⌘/Ctrl+Enter 把结果加入对话。短到没有意义的拖动会被忽略;而一旦你画了东西,在编辑器外误点一下也不会把你的标注丢掉。
智能体收到的是这张图片,外加每个标注一行说明——box: W×H at (x, y)、arrow: pointing at (x, y), from (x, y)——用的是页面的 CSS 像素,并附注说明这些标注不属于页面本身。一个标注都不画就发送,它就是一张普通截图。
让智能体在页面上干活
反方向:智能体可以列出你打开的标签页、读取你已共享页面的结构、在上面动手——点击、悬停、输入、按键、选择选项——截图、读取页面打印到控制台的内容,以及打开、跳转和关闭标签页。
有两件事必须先成立。其中一件从关闭开始,另一件不是:
- 这个工具组对每个智能体默认关闭,直到你打开它。 入口在设置 → 协作 → 会话内工具 → 读取和操作内置浏览器。与那张卡片里的多数开关不同,它在工具被调用的那一刻重新读取,而不是只在智能体启动时读一次——所以关掉它会拦住正在跑的会话,而不只是下一个。
- 页面必须已被共享——而开箱即用的情况下它会自己共享自己。每个标签页在地址栏左侧都有一个控件(只读、可读可操作,或者什么都不给),但默认共享级别这项设置的出厂值就是可读可操作,它会在页面加载时把标签页所到的每一个新站点授权出去,不会过问任何人。已共享的标签页会在地址栏上挂一枚徽标——已共享,或已共享 · 可操作——标签条上也有相应标记,一按即可收回。
所以真正的闸门是第 1 步的那个开关:在智能体拿到这个工具组之前,这一切不会交给任何人。
这一条值得想清楚
一旦某个智能体拿到了工具组,且常驻级别不是不共享,页面会在加载时被共享——包括它用 browser_open_tab 自己打开的页面——凡是那个标签页还没被你另行交代过的站点都算。如果你更希望每一次共享都是你在标签页里做出的决定,请把默认共享级别设为不共享。
共享覆盖什么,何时结束
一份授权属于一个标签页和一个 origin——也就是授权当时页面所在的那个站点。离开那个 origin 授权即结束;接下来会发生什么,取决于常驻级别:
- 设了常驻级别时(出厂默认),下一个 origin 会在页面加载时被授权,不会打断你——每次跨站点击都弹一条警告条只会变成噪音,而工具栏徽标本来就写着当前页面的共享级别。但标签页会按站点记住你做过的决定:你在某个站点上停掉的共享,这个标签页再回到那里时依然是未共享;你收窄到只读的站点,回来也还是只读。常驻级别只替那些这个标签页还没有任何决定的站点说话——包括它没去过的站点,以及它在常驻级别为不共享时到过的站点(那种情况刻意不记录,好让你之后把级别打开时能够到眼前这个已经开着的页面)。所以一次 SSO 跳出再跳回,洗不掉你已经结束的共享。这份记忆与标签页同寿:重启后恢复的标签页不带任何记忆,常驻级别会重新对它生效。缺口在另一头:如果标签页落到了没有任何授权能绑定的地方,共享就此结束,没有东西接上,也没有东西告诉你。
- 选了不共享时,离开 origin 会结束共享并由一条提示条说明,同时提供改为共享新地址的按钮。
localhost 地址上的共享还可能因为那个端口被另一个程序接走而结束——但仅限于 Codeg 在你共享时能识别出当时的服务程序。识别不出来的授权不会被复查,行为与没有这项功能时完全一样。
它做了什么你看得见
每一次读取、每一个动作、每一次被拒,都写在事情发生的那个标签页上——地址栏里的智能体在这里做了什么,旁边带计数。被拒的那些才是关键:智能体在你只给了只读的页面上尝试点击,会留下一行「已拒绝」,于是「共享给窄了」和「智能体什么也没干」看起来不一样。
有两件事不会留下记录,原因是同一个——没有标签页可以承载它,或者它压根不发生在某个页面上。关闭标签页是以「它消失了」来宣告成功的,所以只有被拒绝的关闭才会被记下;而列出你打开了哪些标签页,不是对任何一个页面做的动作。
智能体实际拿到什么
| 需要 | 工具 |
|---|---|
| 除工具组外无需条件 | browser_list_tabs、browser_open_tab——此时还没有标签页可以承载授权 |
| 标签页已共享 | browser_snapshot(页面的无障碍树,每个元素带一个 ref)、browser_console_messages、browser_screenshot |
| 标签页共享为可操作 | browser_click、browser_hover、browser_type、browser_press_key、browser_select_option——以及 browser_navigate、browser_close_tab,但标签页里没有需要保护的页面时(空白页,或显示着错误的页面)除外 |
| 共享为可操作,外加它自己的开关 | browser_eval——也是唯一一个你可以要求每段都摆给你看的工具 |
browser_list_tabs 给智能体的是每个标签页的 origin——带端口的 scheme://host,不含路径与查询串——以及它是否已共享,但在共享之前不给标题:标题是页面自己挑的文字,没共享的页面不该有机会自述;地址则照给,因为你要做共享决定就得看见它。被拒返回给智能体的是一个答案而不是错误,所以你没共享的标签页只会结束这次工具调用,不会结束整轮对话。
让它运行自己的代码
在内置浏览器中执行代码是另一个开关,默认关闭,而且没有上面那个开关它形同虚设。它同样要求页面共享为可操作,仅仅只读是不够的。
三样都齐了之后,代码就会运行——而你正是在那个开关上决定了这件事。到 0.31.1 之前,每一段代码都会改为先摆到你面前,无条件,而且答案从不被记住:打开过这个开关、又把标签页共享为可读可操作的人,其实早就做完了决定,而第一百个对话框是被条件反射答掉的,不是被读完的。于是这份分量往上挪了——在你做决定的时候问你一次,而不是在你干活的时候问你一百次。开关自己的说明文字现在也这么写。
没挪的是任何一道闸门。工具组、这个开关,以及标签页的可读可操作授权,都在请求被发起之前由后端读取,智能体一个也够不着。而静默运行不等于没人看见:它和其他动作一样,会写进那个标签页的智能体活动里。
想要回从前那种方式,一个设置就够:设置 → 浏览器操作 → 在页面上运行代码 → 每次都问我。此后每一段代码都会连同它将运行的站点原样摆出来,等你按运行这一次或不要运行——同意这一段绝不等于同意下一段,代码旁边也刻意没有「总是允许」按钮。回答的是拥有该标签页的那个窗口。被拒绝、或者根本没人回答的调用,回到智能体那里都是一次「未获批准」,并且它会被告知不要重试。
仅限桌面:浏览器会话里没有原生标签页,所以这些工具压根不会通告给那里的智能体。连接到远程工作区的桌面窗口同样不会把它的页面共享给智能体——那个工作区的智能体运行在它的主机上,而不是这台电脑上。
站点规则
设置 → 浏览器操作 → 站点规则是一张小表,在任何默认设置之前按站点决定:
| 动作 | 含义 |
|---|---|
| 内置浏览器 | 始终在这里打开,无论来源默认是什么(⌘/Ctrl 点击仍可反转)。 |
| 系统浏览器 | 始终交出去,比如拒绝在内嵌视图里登录的站点。 |
| 阻止 | 绝不打开——链接不行,地址栏不行,页面链接或重定向过去不行,iframe 里不行,弹窗也不行。 |
模式可以是主机名(wiki.example.com)、通配后缀(*.example.com——匹配 a.example.com 和 a.b.example.com,不匹配 example.com 本身)或 *,后面可选 :端口(localhost:3000、[::1]:8080)。最具体的匹配生效:精确主机胜过通配,更长的后缀胜过更短的,* 排最后,钉住端口的在平手时胜出——所以这张表不需要排序。
由管理员设定的规则
管理员可以用一个小 JSON 文件为机器上的每位用户钉死规则,也可以整体关闭内置浏览器:
| 平台 | 路径 |
|---|---|
| macOS | /Library/Application Support/codeg/policy.json |
| Windows | %ProgramData%\codeg\policy.json |
| Linux | /etc/codeg/policy.json |
CODEG_POLICY_FILE 可以指向另一个文件。该文件在启动时读取一次:
{
"browser": {
"enabled": true,
"hostRules": [
{ "pattern": "*.internal.example", "action": "block" },
{ "pattern": "sso.example.com", "action": "system" }
]
}
}两个键都是可选的。管理员规则在设置表里带锁显示、不可编辑;它们先于用户自己的规则被查询,所以更具体的用户规则也解除不了管理员的阻止。"enabled": false 时每个链接都走系统浏览器,设置区块会说明原因。格式错误的规则会被跳过并记入日志;格式错误的文件会记入日志并被忽略,而不是悄悄解除或施加限制。
重启后的标签页
浏览器标签页像浏览器的一样能挺过重启。Codeg 按窗口记住每个标签页实际所在的页面、标题和设备;下次启动时它们回到标签条上,淡显且未加载——切到某个标签页时它才开始加载,所以恢复二十个标签页只花二十条小记录,不占内存。⇧⌘T / Ctrl+Shift+T 重新打开刚关闭的浏览器标签页,回到它当时显示的那一页,设备也不变——除非同一个配置文件里已经有另一个标签页开着这个地址,那样被带到前面的会是那一个。历史、滚动位置和表单内容不跨重启保留。
还有一个可选的卸载后台标签页开关(默认关):在后台待满三十分钟的浏览器标签页会释放它的引擎,回来时重新加载,期间在标签条上淡显。
配置文件、浏览数据与代理
内置浏览器有自己的配置文件:cookie、缓存和站点存储与 Codeg 自身的数据分开存放,在 macOS 14 及更高版本上还有独立的存储。登录一次站点,重启后依然是登录状态。
配置文件可以不止一个——多一套 cookie 与登录状态,用来在同一站点登第二个账号,或把工作和个人的会话分开。设置 → 浏览器操作 → 配置文件列出它们:按名字新建、清除某一个的浏览数据(会让你从该配置文件里的站点登出,但不触碰 app 自己的设置)、删除某一个(它的标签页会被关闭、数据被删除),并选择新标签页使用哪个配置文件。标签页的配置文件在它的生命周期内固定不变:一旦存在默认之外的配置文件,标签页工具栏就会显示一枚带配置文件名字的标记,标记的菜单可以把同一页面在另一个配置文件里打开——作为旁边的新标签页。弹窗沿用打开它的标签页的配置文件;从某个标签页 ⌘-点击打开的标签页落在那个标签页的配置文件里;重启后恢复的标签页回到它们原来的配置文件(若那个配置文件已被删除,则回到默认)。默认之外的配置文件需要 macOS 14 及更高版本;更早的 macOS 上,该区块只显示一行浏览数据 → 清除…。
Google 登录兼容(默认开;macOS 与 Windows)让浏览器标签页只对 Google 的登录页面——accounts.google.com 与 accounts.youtube.com,仅此两处——呈现 Firefox 身份。Google 拒绝在它不认识的内嵌浏览器里登录;其他地方保持引擎自己的身份,因为反机器人检查会拒绝与发送方引擎不符的身份。有同样拒绝行为的其他登录提供方可以通过环境变量 CODEG_BROWSER_SIGN_IN_HOSTS 加进来(逗号分隔的主机名列表;子域名也算)。两个限制:只在嵌入式标签页里生效,独立窗口不适用;在 macOS 上,登录页把你跳转到别的站点时——比如登录提供方把你送回正在登录的应用——到那个站点的第一个请求仍带着登录身份,之后的请求都用引擎自己的(Windows 上身份按请求判定,跳转过去的第一个请求就已经是引擎自己的身份)。
浏览器标签页使用设置 → 系统 → 网络代理里配置的代理,与 app 和智能体用的是同一个。macOS 上这需要 14 及以上版本:在那之前浏览器标签页根本无法走代理,而且在配置了代理的情况下,标签页会直接打不开,而不是绕过代理去连网——设置里的那一行就是这么写的。在支持的情况下,开着的标签页在下一次连接时就换过去;Linux 上对之后打开的标签页生效;Windows 上需要重启(WebView2 在进程内第一个浏览器标签页创建时固定代理)。本机地址——localhost、127.0.0.1、::1——永远不走代理,所以无论代理怎么处理,开发服务器都照常可用。支持 http:// 与 socks5:// 代理;不支持 https:// 代理。
同一设置区块里的网页检查器开关默认是开的:它把打开检查器放进标签页的 ⋯ 菜单,并允许页面自己的右键菜单里出现「检查元素」。检查器开在自己的窗口里,而不是停靠进标签页——停靠会占掉半个文件面板、把页面挤到角落里,这对浏览器标签页是笔划不来的买卖。Codeg 是把它注册为偏好、而不是强制,所以你在检查器自己的工具栏上做的停靠或取消停靠会被引擎记住,并从那以后压过 Codeg 的选择。和它下面的标签页承载方式一样,这个开关对此后打开的标签页生效,因为 webview 创建之后就无法再改成可检查。标签页承载方式决定页面如何被承载:除非嵌入标签页在你的系统上表现异常,否则保持自动——独立窗口会把每个标签页开成独立窗口,而到 0.35.2 之前,这是 Linux 显示标签页的唯一方式。
浏览器会话里:端口桥
通过 codeg-server 使用 Codeg 时,链接里的 localhost 指的是服务器的回环地址,你的浏览器到不了。这类链接由服务器出面:它为每个开发服务器多绑定一个端口(取自它自己端口之后的十个端口,或 CODEG_BRIDGE_PORTS),把它转发到该开发服务器,工作台则把页面放在那个端口上的标签页里显示。没有任何改写——页面就像在主机上一样从 / 提供,所以模块导入、客户端路由和热更新都照常工作。
- 只桥接服务器回环地址上的纯
http://地址(localhost、127.0.0.1、[::1]、0.0.0.0)。公网地址仍在新浏览器标签页里打开;私网主机不归服务器转发。 - 标签页工具栏有设备按钮、重新加载、在新标签页打开(同一桥端口上的完整浏览器标签页,给 frame 显示不了的东西用)和复制地址。切走再切回该标签页会重新加载页面。
- 访问按会话计:打开页面时会铸造一个短凭据,标签页用它换取桥端口上的一个 cookie。最后一个标签页关闭一分钟后、或两小时没有请求后,桥端口关闭。没有先从已登录的工作台打开,谁也无法经由桥接触到开发服务器。
- 如果 frame 提示无法访问端口桥,说明端口在服务器上绑定了但没有发布到你的浏览器:开放该端口段(Docker:在
3080之外再发布3081-3090,或CODEG_BRIDGE_PORTS指定的范围),或在反向代理给桥换了主机名时设置CODEG_BRIDGE_PUBLIC_HOST。通过 HTTPS 提供的工作台,桥端口也需要放在 HTTPS 之后。
除非 CODEG_BRIDGE_PUBLIC_HOST 给桥指定了独立的主机名,页面与工作台同处一个主机名。在同处一个主机名的情况下:Codeg 自己的 cookie 里,页面能看到语言 cookie,而绝对看不到你的访问令牌——后者存放在仅限工作台自身 origin 的存储里;而那个主机名上其它的 cookie,页面都读得到,桥也会随它的请求一起转发给开发服务器,Codeg 自己的那些则先被剥掉。同时打开的两个开发服务器也无法经由对方的桥端口互相访问:桥端口只回应浏览器标记为「来自该页面自身」的请求——浏览器发 Fetch Metadata 时(HTTPS,或本机)看它,否则看请求的 Origin 或 Referer。两个后果:纯 HTTP 部署下在地址栏手敲桥地址会被拒(请从 Codeg 打开);页面从别的站点回到开发服务器——比如登录提供方的跳转——同样被拒,需要从 Codeg 重新打开。
按主机名而不是按端口
在代理后面使用端口桥时,最别扭的就是要发布一整段端口。自 0.32.0 起,服务器可以改用主机名来指代开发服务器:设置 CODEG_BRIDGE_HOST_PATTERN 之后,3000.codeg.example.com 就会经由 Codeg 已经在监听的那个端口到达 3000 端口——不额外绑定任何端口,也没有端口段需要发布。
auto把端口号放在访问工作台所用的主机名前面——工作台在codeg.example.com上时是3000.codeg.example.com,在localhost上时是3000.localhost——设置了CODEG_BRIDGE_PUBLIC_HOST时则放在它前面。它要求工作台是通过名字访问的:通过 IP 地址访问时,没有名字可供端口号放在前面。- 一个模板,其中恰好包含一次
{port}——{port}.preview.example.com、p{port}-codeg.example.com——用于你自己的命名方案。让它和工作台处在同一个站点下,因为桥的访问 cookie 不会发往另一个站点上的主机名。 - DNS 和代理必须一致。需要一条泛域名 DNS 记录把这些名字指向服务器,前面的反向代理也需要一个泛域名虚拟主机,并原样传递
Host头——在 Caddy 里就是*.codeg.example.com { reverse_proxy codeg:3080 }。页面无法访问时,标签页会准确说明这一点。 - 无法解析的值会关闭端口桥,而不是去猜;即便设置了模式,
CODEG_BRIDGE_PORTS=off也依然能把它关掉。
只有 Codeg 真正发出过的名字才会得到应答。凡是浏览器访问 Codeg 的地方都会读取这个变量:codeg-server,以及桌面应用自带的 Web 服务。
在远程工作区窗口中
连接到远程工作区的桌面窗口自 0.32.2 起也有内置浏览器——而对它最要紧的,恰恰是那些位于远程主机上的地址:智能体在那边打印出来的 localhost:3000、某个容器的私网 IP。这些地址会在远程标签页里打开,它的每一个连接都从远程主机发出,经由那台主机上的 codeg 服务器,所以你看到的就是远程机器所提供的内容。
- 哪些地址算远程主机的。回环地址(
localhost、127.0.0.1、::1、*.localhost)和私网地址(10.x、172.16–31.x、192.168.x、链路本地地址、*.local)——远程服务器自己的名字或地址除外,这台电脑是直接访问它的(除非它本身就是一个回环地址,比如你通过 SSH 隧道连到那台服务器)。指向这类地址的链接总是以远程标签页打开,事先只会查阅起阻止作用的站点规则;在本地标签页的地址栏里输入这样的地址,会在旁边开一个远程标签页。公网地址则作为普通标签页、从这台电脑打开。 - 远程标签页里的一切都经由远程主机——包括页面加载的公网站点。
- 打不开的页面会从远程一侧说明原因——那边的这个地址上没有东西在监听、远程主机到它没有路由、服务器不肯打开的地址、超时,或者到远程主机的连接断了。标签页上有一条写着经由 {host} 打开的横幅,那条连接断开时它会变红。
- 远程服务器必须是 0.32.2 或更高版本;更早的版本没有隧道可提供,标签页会如实说明。它的运维者可以用
CODEG_BROWSER_TUNNEL收窄远程标签页能访问的范围——all(默认)、private(仅回环、私网和链路本地地址)或off。→ 配置 - 在 macOS 上,远程标签页需要 macOS 14 或更高版本,并且会在地址栏里把远程主机的
localhost显示为remote.localhost,因为 WebKit 会让回环地址绕过任何代理。从它复制出来的链接、以及你发到对话里的页面,带的都是真实地址。 - 标签页根本打不开时——没有隧道、服务器版本太旧、macOS 版本太旧——会显示一个占位页,说明这个地址在远程主机上并给出原因,同时提供复制地址;对带端口的
localhost地址提供尝试 {host},它会从这台电脑用远程主机自己的名字打开那个端口;对私网地址则提供在这台电脑上打开。 - 智能体在这里用不了浏览器。远程工作区的智能体运行在它的主机上,而不是这台电脑上,所以在远程窗口里,与它们共享页面的功能是关闭的。
远程标签页没有配置文件菜单,也没有在系统浏览器中打开。→ 隐私与安全
各平台的差异
| macOS | Windows | Linux | |
|---|---|---|---|
| 页面承载 | 嵌入工作区 | 嵌入工作区 | 嵌入工作区,X11 与 Wayland 下都是——自 0.35.2 起 |
| 失败文案 | 按类型(找不到 / 不安全 / 连不上) | 按类型,域名找不到的除外 | 按类型;域名找不到的情况,只有在引擎如此上报时才会单独区分 |
| 弹层下的最后一帧 | 有 | 有 | 有 |
| 页内查找 | 有 | 有 | 有 |
| HTML 文档视图 | 有 | 有 | 尚无(内联预览) |
| 默认之外的配置文件 | macOS 14 及更高版本 | 有 | 有 |
| Google 登录身份 | 有(嵌入式标签页) | 有(嵌入式标签页) | 尚无 |
| 代理变更生效 | 开着的标签页 | 重启后 | 新标签页 |
| 远程工作区标签页 | macOS 14 及更高版本 | 有 | 有 |
| 平板、手机和自定义视图 | 在面板里加边框 | 在面板里加边框 | 在面板里加边框 |
到 0.35.2 之前,Linux 会把每个标签页都开成一个独立窗口;现在它的标签页和其他平台一样待在文件面板里——你打开的每个标签页各有一个自己的 web 进程,弹出窗口则与打开它的那个标签页共用一个——而当弹层盖住一个你正在输入的页面时,键盘焦点会回到工作区。标签页承载方式下的独立窗口在三个平台上都还保留着,留给嵌入表现异常的系统使用。
把页面共享给智能体、把页面递给对话,以及本机服务菜单,三端行为一致。