
OpenWork 桌面应用策略体系从云端 DesktopConfig 下发到前端门控钩子的完整实现【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openworkOpenWork 桌面应用通过组织级的桌面策略Desktop Policy实现对企业成员本地能力的远程管控管理员在云端配置策略桌面端通过GET /v1/me/desktop-config拉取配置并借助DesktopConfigProvider与一组 React 钩子在 UI 上执行门控。本文以 desktop-app-policies.md 为主线结合packages/types、桌面应用前端与 Den API 的源码实现完整讲解策略目录的定义方式、配置下发链路、四种消费钩子的选型与用法、加载刷新机制以及服务端的生效计算帮助你安全、规范地在 OpenWork 桌面应用中接入或扩展策略门控能力。策略体系总览配置从哪里来、到哪里去桌面应用策略配置的完整链路分为三层服务端计算Den API 的GET /v1/me/desktop-config路由见 ee/apps/den-api/src/routes/me/index.ts根据当前登录成员所属组织、其默认策略与已分配策略调用calculateDesktopPolicyForOrgMember()计算生效策略并叠加环境开关与组织品牌元数据后返回。前端状态层桌面应用的DesktopConfigProvider见 apps/app/src/react-app/domains/cloud/desktop-config-provider.tsx负责缓存、拉取、对比并应用配置通过 Context 对外暴露config、loading、refresh()与checkRestriction()。UI 消费层业务代码只应通过该 Provider 导出的钩子useCheckDesktopRestriction、useDesktopRestriction、useDesktopConfig、useOrgRestrictions读取策略状态。不要直接读取 Provider 内部的 ref 字段——那些 ref 仅用于在加载新配置时做安全对比与应用源码注释明确声明了这一点。一个典型的GET /v1/me/desktop-config响应由服务端组装大致如下{ allowCustomProviders: true, allowZenModel: false, allowMultipleWorkspaces: true, allowControlSettings: true, allowManageExtensions: true, allowBuiltInExtensions: true, allowAlphaUpdates: true, showWelcomePage: true, execution: { commands: deny, blockedCommands: [git push origin main --force], browserOrigins: [https://docs.example.com], blockBrowserUploads: false }, allowedDesktopVersions: [1.2.3, 1.2.4-beta.1], brandAppName: Acme Work, brandAccentColor: blue, automationsEnabled: true, dashboardEnabled: true, connectEnabled: false, onboardingPrompts: [帮我梳理本周代码评审, 总结当前分支改动, 起草发布说明], onboardingPromptDescriptions: [代码评审, 改动总结, 发布说明] }其中布尔策略键决定功能是否受限而execution、allowedDesktopVersions、品牌字段、onboardingPrompts等非布尔字段则承载命令执行、版本白名单、品牌定制与新用户引导等扩展能力。策略目录desktopPolicyDefinitions 是唯一的权威清单规范的策略目录canonical policy catalog位于 packages/types/src/den/desktop-policies.ts以desktopPolicyDefinitions数组定义。新增或修改策略项时必须先改这里这样 API、Den Web 与桌面应用才能共享同一套 ID 与文案name、teamLabel、description、userNotice。不要在任何应用文档或功能代码里重复维护一份策略 ID 列表除非某个功能确实在单独检查某个策略需要完整目录时从共享包导入import { desktopPolicyKeys } from openwork/types/den/desktop-policies;当前策略项一览每个定义条目包含id、name、teamLabel、description、userNotice、defaultValue以及restrictedValuegroup受限模式锁定值与其所属分组。当前目录共 8 个布尔策略id分组名称teamLabel默认值Restricted 模式锁定值allowCustomProvidersaiAdd AI providerstruefalseallowZenModelaiUse OpenCode modelstruefalseallowMultipleWorkspacesappCreate more workspacestruefalseallowControlSettingsappChange app settingstruefalseallowManageExtensionstoolsAdd local tools, skills MCP serverstruefalseallowBuiltInExtensionstoolsUse built-in extensionstruefalseallowAlphaUpdatesappTry experimental updatestruefalseshowWelcomePagedisplayShow welcome pagetruenull不锁定保持可编辑类型定义DesktopPolicyDefinitionEntry强制要求每个受限能力都必须能在团队编辑器中找到对应位置而显示类偏好group: display如showWelcomePage在 Restricted 模式下仍保持可编辑因此其restrictedValue为null。布尔语义与校验 Schema布尔策略键语义false表示该功能被限制或禁用true或undefined表示应用不应在本地阻止该功能。Schema 自动生成desktopPolicyValueSchema由定义列表中的 ID 自动生成每个键都是z.boolean().optional()不要手工编辑。源码注释给出了新增策略的六步流程加定义 → 选择安全默认值 → 选择restrictedValue→ 用钩子接线桌面行为 → 如需影响 Den Web 编辑文案则统一维护name/description/teamLabel/group/userNotice→ 不要手改 Schema。从定义派生出的辅助导出包括DesktopPolicyKey、desktopPolicyKeys、desktopPolicyDefaults以及desktopPolicyUserNotices应用在策略阻止能力时展示的用户提示文案。非布尔配置项allowedDesktopVersions是 desktop config 响应的一部分但它不是desktopPolicyDefinitions中的布尔策略项。它在响应 SchemadesktopConfigSchema中作为独立数组存在且经过版本字符串归一化去除首字母v并要求匹配\d\.\d\.\d加可选 pre-release/build 段的正则非法项被过滤、重复项被去重见 packages/types/src/den/desktop-policies.ts。执行策略与文档 Schema不止布尔开关除布尔开关外策略文档还支持access团队访问与execution执行限制两个结构化字段见desktopPolicyDocumentSchemaexecution.commandsallow或deny默认allow一旦任一匹配策略为deny生效结果即为deny。execution.blockedCommands被阻止的命令模式数组单条长度 1–500最多 100 条。execution.browserOrigins允许的浏览器来源必须是http:/https:、无路径、无凭据、无 query/hash 的站点最多 100 条多个策略间取交集。execution.blockBrowserUploads是否阻止浏览器上传任一策略为true则生效。resolveDesktopExecutionPolicy()将多个策略文档的执行配置合并为一份生效值命令模式取并集去重、浏览器来源取交集、上传限制取或。这套执行策略正是 desktop-policy-engine-rollback.md 中所述命令与浏览器控制仍然可见的底层依据——原生内置的浏览器请求、来源与上传门控不依赖被移除的 managed 插件。组织 Prompt 建议新用户引导卡片桌面策略文档还可以携带onboardingPrompts与onboardingPromptDescriptions用于组织自定义新用户的引导内容。共享 Schema 的约束如下见 packages/types/src/den/desktop-policies.tsonboardingPrompts要求2 或 3 个prompt每个 trim 后 1–500 字符onboardingPromptDescriptions要求2 或 3 个描述每个 trim 后最多 120 字符且描述数组长度必须与 prompt 数组一致。在桌面应用中onboardingPromptDescriptions成为 prompt 卡片的标题onboardingPrompts成为卡片上可见的描述同时也是用户点击卡片时插入到 composer 中的文本点击不会自动发送用户仍需自行触发。Prompt 配置的选择逻辑与布尔策略不同布尔策略键由calculateEffectiveDesktopPolicy()对匹配策略做OR 并集而 Prompt 建议走的是另一条路径selectEffectiveOnboardingPromptConfig()实现见 packages/types/src/den/desktop-policies.ts按以下次序只选一个匹配的 prompt 配置priority最高者优先优先级相同时取createdAt最早者仍相同时按策略id字典序localeCompare决定没有任何定向 prompt 配置命中时回退到默认策略的 prompt 配置。服务端在calculateDesktopPolicyForOrgMember()中同样调用selectEffectiveOnboardingPromptConfig完成计算保证 API 返回值与桌面端选择逻辑一致。门控首选useCheckDesktopRestriction()当需要给应用行为做门控时优先使用useCheckDesktopRestriction()。它返回一个稳定的检查函数入参为策略键返回true表示该功能被限制与策略键的false语义互为镜像import { useCheckDesktopRestriction } from ../domains/cloud/desktop-config-provider; function Example() { const checkDesktopRestriction useCheckDesktopRestriction(); const zenModelsRestricted checkDesktopRestriction({ restriction: allowZenModel, }); return zenModelsRestricted ? null : ZenModelPicker /; }该钩子的底层实现desktop-config-provider.tsx返回 Context 中稳定的checkRestriction函数其形态与 Solid 版本 store 传入的DesktopAppRestrictionChecker一致便于在非 Hook 代码路径中复用。单策略钩子useDesktopRestriction(key)当组件只需要一个策略值时用useDesktopRestriction()更简洁——它内部就是对checkRestriction({ restriction })的封装import { useDesktopRestriction } from ../domains/cloud/desktop-config-provider; function AddWorkspaceButton() { const multipleWorkspacesRestricted useDesktopRestriction( allowMultipleWorkspaces, ); return ( button disabled{multipleWorkspacesRestricted} Add workspace /button ); }返回值同样是是否被限制的布尔值适合直接驱动disabled、隐藏分支等一次性判断。原始配置useDesktopConfig() 与 useOrgRestrictions()当需要原始配置、加载状态或手动刷新函数时使用useDesktopConfig()。它返回完整的DesktopConfigStore包含config、loading、freshConfigStatus、refresh()、refreshFresh()、checkRestriction与connectPolicySyncimport { useDesktopConfig } from ../domains/cloud/desktop-config-provider; function DesktopPolicyDebug() { const desktopConfig useDesktopConfig(); return ( pre {JSON.stringify({ loading: desktopConfig.loading, config: desktopConfig.config, }, null, 2)} /pre ); }useOrgRestrictions()只在需要不带loading、refresh、checkRestriction的裸配置对象时使用——它直接返回useDesktopConfig().config见 desktop-config-provider.tsximport { useOrgRestrictions } from ../domains/cloud/desktop-config-provider; function Example() { const config useOrgRestrictions(); const customProvidersRestricted config.allowCustomProviders false; return customProvidersRestricted ? RestrictedNotice / : ProviderForm /; }模型与 Provider 专用助手函数针对模型/Provider 的门控使用 apps/app/src/app/cloud/desktop-app-restrictions.ts 中的助手函数checkDesktopAppRestriction({ config, restriction })核心判定即config[restriction] falseisDesktopProviderBlocked({ providerId, checkRestriction })当 providerId 为opencode时映射到allowZenModel策略isDesktopModelBlocked({ model, checkRestriction })基于模型的providerID转发到上者isSettingsTabAllowed({ tab, checkRestriction })当allowControlSettings被限制时仅保留cloud-account标签页其余设置页隐藏并重定向desktopRestrictionNotice(restriction)读取desktopPolicyUserNotices中的组织提示文案。结合钩子的典型用法import { isDesktopModelBlocked } from ../../app/cloud/desktop-app-restrictions; import { useCheckDesktopRestriction } from ../domains/cloud/desktop-config-provider; function ModelOption({ model }: { model: ModelRef }) { const checkDesktopRestriction useCheckDesktopRestriction(); const blocked isDesktopModelBlocked({ model, checkRestriction: checkDesktopRestriction, }); return ModelRow model{model} disabled{blocked} /; }加载与刷新机制缓存优先 事件驱动 小时级兜底DesktopConfigProvider的加载策略是先读缓存、再取最新具体流程见 desktop-config-provider.tsx从localStorage同步读取该组织缓存缓存键为baseUrl::activeOrgId见 den.ts 中的 getDenDesktopConfigCacheKey同步应用保证被门控的 UI 不会因为 HTTP 未完成而闪现未受限状态调用createDenClient(...).getDesktopConfig(activeOrgId)发起GET /v1/me/desktop-config客户端封装见 den.ts成功后写回缓存、diff 并应用变更失败则回退到缓存无缓存回退空配置并把freshConfigStatus置为failed若服务端返回organization_not_found404会触发ensureDenActiveOrganization({ forceServerSync: true })重新同步组织供下次刷新命中有效组织。刷新时机三路触发登录 / 会话变化denSessionUpdatedEvent与 Den 设置变化denSettingsChangedEvent事件一小时定时器DESKTOP_CONFIG_REFRESH_MS 60 * 60 * 1000见 desktop-config-provider.tsx手动刷新useDesktopConfig().refresh()也提供refreshFresh()强制要求新鲜数据失败即抛错const desktopConfig useDesktopConfig(); await desktopConfig.refresh();此外 Provider 还承担了配置的副作用应用brandAppName会写入document.title并同步到 shell、brandIconUrl会通过 IPC 应用到桌面图标且会把品牌信息回写到desktop-bootstrap.json防止清除品牌后在下次启动时被快照复活syncBootstrapBranding。开发模式下还暴露了window.__openworkApplyDesktopConfig等桥接接口便于 eval 直接注入配置。服务端视角生效策略如何计算GET /v1/me/desktop-config的处理流程desktop-policies.ts查询该组织下所有未删除的桌面策略含isDefault、isEnabled、priority、createdAt组织没有任何策略时直接返回全部放行allDesktopPolicies(true)找到默认策略isDefault isEnabled再查询成员角色、所属团队通过matchingDesktopPolicyAssignmentRoles与团队/成员分配表找到所有匹配的已分配策略调用共享包中的calculateEffectiveDesktopPolicy()以全部false为起点把默认策略与所有已分配策略中值为true的键做 OR 并集随后对每个携带access团队访问字段的策略按mode为custom直接用 capabilities或locked用applyRestrictedDesktopPolicy锁定为受限值解析出能力把其中为false的键写回结果——访问限制是对布尔并集之后的二次收窄见 packages/types/src/den/desktop-policies.ts。最后路由把计算出的策略与以下字段组装成响应me/index.tsautomationsEnabled来自env.automations.enableddashboardEnabled来自env.dashboardsEnabledconnectEnabled来自memberFacingMcpConnectionsEnabled(...)allowedDesktopVersions、brandAppName、brandLogoUrl、brandIconUrl、brandAccentColor来自组织元数据normalizeOrganizationMetadata。使用准则API 选型顺序按照官方文档给出的优先级选用 APIuseCheckDesktopRestriction()——绝大多数功能门控的首选useDesktopRestriction(key)——单次一次性组件检查useDesktopConfig()——需要 loading / refresh / 原始配置时useOrgRestrictions()——仅做裸配置读取时。常见注意事项不要绕过钩子应用代码一律通过上述钩子读取策略状态Provider 内部的 ref如currentDesktopConfigRef只用于配置对比与应用直接读取会破坏其设计约定。不要重复维护策略 ID需要完整目录时从openwork/types/den/desktop-policies导入新增/改名策略先改desktopPolicyDefinitions。布尔语义以false为受限true或缺失表示本地不应阻止allowedDesktopVersions等非布尔字段不参与此语义。Prompt 与布尔策略的计算路径不同前者按 priority → createdAt → id 单选后者按 OR 并集叠加混用两种心智模型容易出错。执行策略与回滚背景命令与浏览器门控的本地执行层独立于被回滚的 managed 插件详见 desktop-policy-engine-rollback.md重启 OpenWork 及其托管引擎后新配置才完全生效生成配置的改写不会卸载运行中引擎已加载的钩子。【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考