首页
/ VS Code 本地 Mock Copilot Policy Server 实战:模拟、代理接入与失败闭环测试企业 Copilot 策略端点

VS Code 本地 Mock Copilot Policy Server 实战:模拟、代理接入与失败闭环测试企业 Copilot 策略端点

2026-09-07 17:18:30作者:咎竹峻Karen

这篇技术指南围绕 VS Code 仓库中的开发调试工具 mock-policy-server(位于 scripts/mock-policy-server)展开,讲解如何在本机启动一个零运行时依赖的 Node 服务与 Web GUI,对 VS Code 账号体系 DefaultAccountService 依赖的四条 Copilot 企业策略端点做「模拟(mock)」或「透传(passthrough)」分流,并系统覆盖 GUI 接入、HTTP 控制 API、文件式策略部署与 fail-closed 刷新测试等完整工作流。读完本文,你将掌握如何在本地复现、观测并人为制造 Copilot 策略下发过程中的各种成功与故障场景,而无需真实企业服务器环境。

它解决的问题与适用边界

VS Code 从服务端拉取企业级 Copilot 策略(Managed Settings、Entitlements、Token 携带的策略标志、MCP Registry)时,访问的是 DefaultAccountService 定义的一组固定端点。真实环境里这些端点在 https://api.github.com 上,由企业配置驱动,难以在本地随意「制造」返回 404、500、畸形 JSON 甚至直接断开连接的响应来测试客户端行为。

mock-policy-server 正是一个仅供本地开发与联调使用的工具:它在 127.0.0.1:3000 启动一个本地 Node 服务并附赠 Web 控制台,把选定端点「留在本地伪造」,其余请求则原样转发给真实上游 API。它没有运行时依赖,也不随 VS Code 产品发布——根据 package.json 中定义的脚本即可确认它的启动入口:

"mock-policy-server": "node --experimental-strip-types scripts/mock-policy-server/server.ts"

它被挂在仓库的 scripts/ 开发目录下,服务端与浏览器端通过 Node 内置的 module.stripTypeScriptTypes() 直接消费 .ts 源码,全程无构建步骤。

四个被模拟的策略端点

端点定义集中在单一事实来源 scripts/mock-policy-server/endpoints.ts,该文件同时被 Node 服务(作为路由表与默认状态)和浏览器 GUI(作为标签页与预设下拉)以 UMD 风格导出共享。四个端点如下:

端点 ID GUI 标签 URL 路径 product.json 默认行为
managedSettings Managed Settings /copilot_internal/managed_settings managedSettingsUrl 默认 mock(mockedByDefault: true
entitlements Entitlements /copilot_internal/user entitlementUrl 默认透传
token Token /copilot_internal/v2/token tokenEntitlementUrl 默认透传
mcpRegistry MCP Registry /copilot/mcp_registry mcpRegistryDataUrl 默认透传

端点 URL 前缀与实际地址来自 product.jsondefaultChatAgent 段的真实配置,例如 "managedSettingsUrl": "https://api.github.com/copilot_internal/managed_settings"。这些路径之所以能被本工具接管,是因为它们处在一条系统代理规则之下,使 Code OSS、Stable/Insiders、Copilot CLI 与 SDK 客户端走同一条策略下发通道。非 mockedByDefault 的端点启动时处于透传状态,被转发到真实 API,从而保证「一条全量代理规则」是安全的——只有你主动开启的端点才会被伪造。

Managed Settings 的 13 个内置预设

managedSettings 是四个端点中预设最丰富、也是企业策略管控最核心的一个,其完整预设表定义在 endpoints.ts。GUI 的预设下拉与 HTTP API 的 preset 字段均直接引用这些 ID:

预设 ID 语义要点 状态码
empty 空对象 = 「无企业策略文件」的成功响应 200
disable-bypass-permissions 封锁所有升级到 bypass-permissions(allow-all/yolo)的能力,含自动批准 200
deny-dangerous-commands 按 managed 权限语法拒绝特定 Shell 命令、工作区级文件写入与域名(单个前导 / 表示工作区根) 200
workspace-scoped-paths 演示相对工作区根的路径:/src/**/test/** 只匹配工作区内,/package.json 精确命中工作区文件 200
ask-before-publish 发布/部署与用户主目录内任意写入需人工批准 200
lockdown-allowlist 只允许批准集合(与其它 managed 白名单求交集),配合 deny/ask 纵深防御 200
sandbox-no-internet 开启运行时沙箱、允许 bypass,但关闭出站网络 200
model-auto 将托管模型设为 auto 200
extra-known-marketplaces 追加带托管自动更新配置的 marketplace 200
customization-lockdown 只允许托管插件/MCP 服务器/hooks,并强制远程刷新 200
not-configured 无服务端托管策略 404
update-required 客户端无法执行有效托管设置而被拒绝 466
server-error HTTP 500,用于走 fail-closed 的 HTTP 错误路径 500

预设中的响应体都是可直接复制的 JSON 样例,例如 customization-lockdown 的关键策略体为:

{
  "strictPluginOnlyCustomization": true,
  "allowManagedMcpServersOnly": true,
  "allowManagedHooksOnly": true,
  "forceRemoteSettingsRefresh": true
}

deny-dangerous-commands 则演示了托管权限语法(ShellWriteDomain 动作 + 工作区根路径语义):

{
  "permissions": {
    "deny": [
      "Shell(rm -rf *)",
      "Shell(curl *)",
      "Write(/.github/workflows/**)",
      "Domain(evil.example.com)"
    ]
  }
}

其余三个端点各带一个基线预设:entitlementsenterprise-enabled(返回 chat_enabled: truecopilot_plan: 'enterprise',是网关端点——只有这里 chat_enabled 为真,后续 token 与 managed settings 才会被拉取);tokenall-enabled(token 串内以分号分隔 agent_mode=1;editor_preview_features=1;mcp=1 等策略标志);mcpRegistryregistry-only(仅允许企业 MCP registry 的 registry_access)。

快速开始

npm run mock-policy-server

然后打开 http://127.0.0.1:3000。默认情况下 Managed Settings 已被 mock;在 GUI 中每个端点标签页旁都有一个开关,用于在 mock / passthrough 之间切换。预设立即生效;响应行为(mode)、状态码与 JSON 编辑内容都会自动保存

GUI 打开后位于 Policies 工作区,顶部有全局连接指示灯与操作入口:

  • 选择 header 中的 Setup 打开设置对话框,引导你完成下列连接方式中的任意一种;
  • 主区域(Live Requests)展示本服务处理过的滚动请求日志,选择一条命中四端点之一的请求即可打开其响应编辑器,非策略端点请求保持只读(响应数据由 server.ts 中以「最新在前」方式维护、上限 200 条的请求日志驱动)。

三种连接方式与文件式部署

方式一:系统代理(推荐)

系统代理对以下客户端全部生效:Code OSS、Stable、Insiders、Copilot CLI,以及 SDK/运行时客户端。任何能改写 HTTPS 请求的 HTTP 调试代理都可行,README 在 macOS 与 Windows 上推荐 Proxyman:

  1. 在 Setup 页拿到面向代理的 Map Remote 规则(把 api.github.com 的这四个策略路径映射到本地 mock server);
  2. 使用页面提供的按平台切换开关,让系统流量经由 Proxyman(macOS 是 Tools > macOS Proxy,Windows 是 Tools > Override Windows Proxy);
  3. VS Code 系列客户端还需要在 settings.json 中加入页面展示的 http.proxy 属性——注意「复制」动作只复制该属性本身,不带外层对象花括号,便于直接追加到既有 JSON 中。

方式二:基于文件的设置(免代理)

完全跳过代理:把企业 managed-settings.json 写到客户端设备上。客户端在启动时登录前零网络往返地直接从磁盘读取该文件,因此这些请求根本不会到达本服务。它适合:

  • 想省去代理配置的情况;
  • 想验证「文件式设置 vs 服务端托管设置」的优先级关系。

该方式仅限本地客户端。文件式设置的部署位置与官方 Enterprise Managed Settings 的 "Deploying file-based settings" 说明一致,各平台路径由 server.tsgetFileDeployment() 常量定义:

操作系统 managed-settings.json 位置
macOS /Library/Application Support/GitHubCopilot/managed-settings.json
Windows %ProgramFiles%\GitHubCopilot\managed-settings.json
Linux /etc/github-copilot/managed-settings.json

文件部署与解除

选用文件式方案时无需「连接」,直接在 Policies 页右侧边栏使用 File Deployment 即可。它提供按平台的命令(macOS、Windows PowerShell、Linux),把当前响应体写入上述 managed-settings.json;复制后在客户端设备上执行,然后重启客户端。macOS 与 Linux 命令带 sudo,因为 Copilot CLI 要求该文件是root 所有、不可写的普通文件。每个平台段落都额外提供一条删除命令用于撤销文件式策略。

连上后(文件式则直接跑 VS Code 命令),打开 VS Code 命令面板执行 > Developer: Sync Account Policy 以刷新策略;若需同步 Local Agent Host 使用的策略,还需再执行 > Developer: Restart Local Agent Host

清除 SDK 策略缓存(Troubleshooting)

如果在 Live Requests 中看不到真实请求,打开右侧边栏的 Troubleshooting,在 Clear SDK policy cache 下展开客户端平台并执行复制的命令。Copilot SDK 把该缓存维护在 VS Code 之外的磁盘上;未清除时,一个新鲜的缓存条目会让客户端在最长一小时内都不发起新请求。缓存位置逻辑(server.ts 的注释与 managedSettingsCacheDirs())与 copilot-agent-runtime 的 path_helpers::copilot_cache_home + managed_settings_cache::CACHE_SUBDIR 对齐:COPILOT_CACHE_HOME 优先,否则按平台缓存基目录(macOS ~/Library/Caches、Windows %LOCALAPPDATA%、Linux ${XDG_CACHE_HOME:-~/.cache})拼上 copilot/managed-settings

macOS 清缓存命令:

rm -rf -- "${COPILOT_CACHE_HOME:-$HOME/Library/Caches/copilot}/managed-settings"

Windows PowerShell 清缓存命令(等价逻辑的完整实现见 README):

$root = if ($env:COPILOT_CACHE_HOME) { $env:COPILOT_CACHE_HOME } elseif ($env:LOCALAPPDATA) { Join-Path $env:LOCALAPPDATA 'copilot' } else { Join-Path $HOME '.cache\copilot' }; $path = Join-Path $root 'managed-settings'; if (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -Recurse -Force }

清完缓存再按上文重新执行 Sync Account Policy。

隔离运行:共享缓存与专用缓存目录

由于其它 Copilot 客户端共享该缓存,如需完全隔离,可以让 server 与 Code OSS 使用同一个临时缓存目录启动:

COPILOT_CACHE_HOME="$PWD/.build/mock-policy-cache" npm run mock-policy-server
COPILOT_CACHE_HOME="$PWD/.build/mock-policy-cache" ./scripts/code.sh

状态持久化:双副本与原子写

README 明确了两处冗余存储:

  • 浏览器侧:GUI 把每个端点的响应体草稿保存在 localStorage(键前缀见 app.ts);
  • 服务端侧:每次更新后 server 把完整状态原子写入 ~/.mock-policy-server/state.json(默认路径常量见 server.ts)。

两份拷贝在服务重启后都能存活。实现上 persistEndpointState()server.ts)先以 0o600 权限写入 state.json.<pid>.tmp,再 renameSync 换名落盘——这正是「原子更新」的语义来源;启动时若发现该文件不可读或校验失败,服务会打印 Ignoring invalid persisted state ... 并继续用默认状态运行。无效 JSON 会一直留在浏览器存储中(server 无法为其服务),直到你在编辑器里改正。

--state-file 或环境变量 MOCK_POLICY_STATE_FILE 可以选择不同的服务端状态文件。

自动连接探测机制

Setup 对话框每五秒通过系统代理发送一次无凭据的探测请求,形状为:

GET <configured upstream>/copilot_internal/managed_settings?mockPolicySetupProbe=<random UUID>

默认 upstream 是 https://api.github.com。要点在于:

  • 探测必须与真实策略流量走同一条 Map Remote 映射,页面只在被映射的响应携带本 mock server 的标识头 X-Mock-Policy-Server(常量见 server.ts)时才判定连接成功;
  • 不检查 Proxyman 或操作系统的代理配置本身,只验证端到端映射是否生效;
  • 顶部全局指示器据此显示绿色/红色连接状态。

为了让这些自动探测不污染 Proxyman 的流量列表,可在其显示过滤器中用正则 ^(?!.*mockPolicySetupProbe).*managed_settings.* 过滤展示;但请不要把探测从 Map Remote 规则中移除,否则连接检查无法到达本服务。在服务端,mock server 会识别该探测查询参数,并把这类请求从 Live Requests 请求日志中剔除(相关处理见 server.ts:带 mockPolicySetupProbe 的请求直接回 204 且不记日志)。

HTTP 控制 API:无需 GUI 的完整配置

控制 API 是纯 JSON 的,机器可读、可脚本化。先读机器可读的索引与当前状态:

BASE=http://127.0.0.1:3000
curl "$BASE/api"
curl "$BASE/api/state"
  • GET /api/state 返回端点 ID、预设、当前响应体、状态码与 mock/passthrough 状态;
  • GET /api 面向人类与 Agent 双端,文档化更新字段类型、合法响应模式、原子更新语义、路由副作用与统一 JSON 错误形状。

服务端对「已知路径 + 不支持的方法」返回 405 并带 Allow 头;未知路径回 404 且响应带 discovery: "/api" 引导回索引(见 server.ts)。

应用预设 / 自定义响应 / 原子批量更新

应用一个已知预设:

curl -X POST "$BASE/api/state" \
  -H 'Content-Type: application/json' \
  -d '{"endpoint":"managedSettings","preset":"not-configured"}'

设置自定义响应(显式覆盖 status/body/active,优先级高于预设):

curl -X POST "$BASE/api/state" \
  -H 'Content-Type: application/json' \
  -d '{"endpoint":"managedSettings","active":true,"status":200,"body":{}}'

一次原子地配置多个端点(先整体校验、后统一应用,任一无效则全部不生效,从源码看更新会被构造为 nextState 副本后整体替换):

curl -X POST "$BASE/api/state" \
  -H 'Content-Type: application/json' \
  -d '{"endpoints":[
    {"endpoint":"managedSettings","preset":"empty"},
    {"endpoint":"entitlements","active":false},
    {"endpoint":"token","active":false},
    {"endpoint":"mcpRegistry","active":false}
  ]}'

更新字段与合法取值(与 server.tsEndpointUpdateapplyEndpointUpdates() 校验逻辑一一对应):

字段 类型/取值 说明
endpoint string(必填) 必须是 GET /api/state 返回的端点 ID
preset string 目标端点支持的预设 ID;会设置 status/body 并开启 mock
status integer 200–599 显式值覆盖预设的 status
body 任意 JSON 显式值覆盖预设的 body
mode json / malformed-json / disconnect / timeout 独立于预设的响应行为
active boolean true mock,false 透传到上游

批量更新先整体校验、后原子应用;无效请求在任何端点变更发生前即被拒绝(400),未知字段、重复 endpoint、未知预设等都会被逐条报告。服务端在每次成功更新后原子持久化。

文件式部署命令生成与其它控制操作

基于当前 managedSettings 响应体生成三平台的文件式安装/删除命令:

curl "$BASE/api/file-deployment"

响应含目标路径以及 macOS/Windows/Linux 的安装与删除命令。server 不会代跑这些命令——请在客户端机器上执行后重启客户端;撤销文件式 Managed Settings 则执行对应的删除命令(或直接删除 managed-settings.json)。

其余控制操作一览:

curl "$BASE/api/schema"
curl "$BASE/api/log"
curl -X DELETE "$BASE/api/log"
curl -X DELETE "$BASE/api/cache"
curl -X POST "$BASE/api/reset"

注意:DELETE /api/cache修改运行 server 那台机器上的文件(删除所有平台候选缓存目录内容)。调用任何带副作用的路由前,建议先查 GET /api 中每条路由的 sideEffects 字段——控制路由表原样定义了 server-state / filesystem / none 三种取值(见 server.ts)。

路由总表

Method Route Purpose
GET /api 发现请求形状与路由
GET /api/state 读取定义、预设与当前状态
POST /api/state 应用并持久化单次更新或原子端点数组
POST /api/reset 恢复并持久化默认端点状态
GET /api/schema 读取 managed-settings schema
POST /api/schema 为当前 server 进程更换并重载 schema 源(仅限 loopback URL)
GET /api/file-deployment 生成文件安装与删除命令
GET, DELETE /api/log 读取或清空请求日志
DELETE /api/cache 清空 managed-settings 磁盘缓存

实战:测 fail-closed 的 Managed Settings 刷新

README 提供了一套演练 fail-closed(关闭即失败)刷新语义的完整脚本。先下发一个「开启强制刷新要求」的成功策略并同步进 VS Code,再配置 HTTP 错误预设或失败响应行为并再次同步——先种下强制刷新要求,是在镜像真实部署中「缓存的管控策略在故障期间自我延续」的场景

curl -X POST "$BASE/api/state" \
  -H 'Content-Type: application/json' \
  -d '{"endpoint":"managedSettings","preset":"customization-lockdown"}'

# 在 VS Code 里运行 "Developer: Sync Account Policy",然后任选其一:
curl -X POST "$BASE/api/state" -H 'Content-Type: application/json' \
  -d '{"endpoint":"managedSettings","preset":"server-error"}'
curl -X POST "$BASE/api/state" -H 'Content-Type: application/json' \
  -d '{"endpoint":"managedSettings","mode":"malformed-json","status":200}'
curl -X POST "$BASE/api/state" -H 'Content-Type: application/json' \
  -d '{"endpoint":"managedSettings","mode":"disconnect"}'
curl -X POST "$BASE/api/state" -H 'Content-Type: application/json' \
  -d '{"endpoint":"managedSettings","mode":"timeout"}'

这四类配置分别覆盖 HTTP 错误畸形响应即时网络失败客户端超时路径。若 Live Requests 中看不到请求,先按上文清策略缓存再重试。

从源码看,四种响应模式的实际行为定义在 sendMockedResponse()server.ts):

  • json:按 entry.statusentry.body 返回 JSON;
  • malformed-json:同样写指定状态码,但响应体是残缺的 {"unterminated":
  • disconnect:记录 status: 0 后直接销毁连接;
  • timeout:挂起 10 秒后销毁连接(timer.unref() 避免阻塞进程退出)。

请求日志会以 mocked / passthrough / upstream-error 三种 outcome 区分每条请求的处置方式(server.ts)。

Schema 来源与启动选项

服务启动时自动探测主 VS Code 检出目录旁的 copilot-agent-runtime/schema/managed-settings-schema.json,并且从 Git worktree 也能找到:resolveDefaultSchemaSource()server.ts)会通过读取 .git 文件(gitdir: 记录)回溯到主检出路径,因此在嵌套 worktree 中运行也能定位仓库(如 /Users/name/git/copilot-agent-runtime)。

替换 schema 源有三种方式:启动参数 --schema、环境变量 MANAGED_SETTINGS_SCHEMA,或在 GUI 中编辑 Schema source 并点击 Load Schema。GUI 的改动只作用于当前 server 进程,server 重启后回到启动时源。由于 schema 源可能是本地文件路径或远端 URL,更换 schema 源被限制为仅接受 loopback 来源的请求isLoopbackRequest() 同时校验远端地址与 Host 均为回环地址,见 server.ts)。schema 源支持三类形态:http(s):// URL、file:// URI、或相对 cwd 的普通路径(相对路径含 .. 会被拒绝)。

带参启动示例(注意 npm 传参需 --):

npm run mock-policy-server -- --upstream https://api.ghe.example.com
npm run mock-policy-server -- --schema /path/to/managed-settings-schema.json
npm run mock-policy-server -- --port 3001
npm run mock-policy-server -- --state-file /path/to/mock-policy-state.json
npm run mock-policy-server -- --help

完整选项表(命令行参数由 parseArgs() 手工解析,--port 必须是 1–65535 的整数,见 server.ts):

Flag 环境变量 默认值
--host 127.0.0.1
--port 3000
--upstream MOCK_POLICY_UPSTREAM https://api.github.com
--schema MANAGED_SETTINGS_SCHEMA 自动探测的兄弟检出
--state-file MOCK_POLICY_STATE_FILE ~/.mock-policy-server/state.json

源码级安全与分流细节

server.ts 的主请求处理流程可以提炼出几条值得注意的工程约束:

  • 控制 API 同源限制/api/* 不设 CORS,且 isAllowedControlOrigin() 要求带 Origin 的请求必须与 Host 同源,否则返回 403——防止任意无关网页驱动本机的文件系统类控制路由;
  • 被 mock 的端点才开放宽松 CORS:只有四端点命中且 active 为真时才带上 Access-Control-Allow-Origin: *,让 Code OSS 的 Web(浏览器)构建能跨源调用;
  • GUI 静态资源白名单:服务仅用显式 Map 服务 //index.html/style.css/app.js(即 public/app.ts,type-strip 后按 JS 提供)、/endpoints.js 等固定资源,避免静态路由遮蔽需要透传的 API 路径;
  • favicon 就地消化:页面自动触发的 /favicon.ico 请求直接回 204,不进代理也不污染请求日志;
  • 透传保真passthrough() 改写 Host、剥掉 hop-by-hop 头、禁用 gzip 以便代理 UI 中可读原始字节,同时原样转发客户端的 Authorization,从而让未被 mock 的端点返回真实结果(server.ts)。

相关实现索引

继续深入阅读当前仓库时,可按下面顺序追踪:

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388