Skip to content

feat(skillhub): 按企业部门策略自动安装技能 - #4

Closed
qbqian wants to merge 3930 commits into
xindong:mainfrom
qbqian:feat/skill-department-distribution
Closed

qbqian wants to merge 3930 commits into
xindong:mainfrom
qbqian:feat/skill-department-distribution

Conversation

@qbqian

@qbqian qbqian commented Sep 21, 2026

Copy link
Copy Markdown

这次改了什么

摘要

企业成员登录、切换身份或重启时,获取服务端按部门计算的技能安装名单,复用现有 Skill Hub 下载、校验和安装流程。原有产品自动同步配置继续生效,企业同名项优先。

变更类型

  • feat 新功能
  • fix 缺陷修复(N/A)
  • refactor / perf 重构或性能优化(N/A)
  • docs / test / chore 文档、测试或工程维护
  • 其他:N/A

范围

  • 关联 Issue / 需求:企业技能按部门自动分发;服务端配套 PR 待关联。
  • 本 PR 包含:分页获取组织安装名单、严格响应校验、身份切换取消检查、同账号重新登录重拉策略、复用现有安装器。
  • 明确不包含:后台页面、服务端权限判断、自动卸载、实时推送。
  • 用户可见变化:符合可见范围和安装范围的企业成员自动获得指定技能;用户原有忽略偏好保留。
  • 是否存在 breaking change:无;旧服务缺少新接口或请求失败时保留原有产品自动同步。部门归属由服务端判断,客户端不提交部门 ID。

UI 变化

不涉及。

  • 引用的设计规范:不涉及:仅修改 main 进程同步逻辑,未改界面、交互控件或文案。

怎么验证的

自动验证

corepack pnpm test:unit:related
结果:PASS test:runner,PASS apps/desktop unit
corepack pnpm --filter desktop typecheck
结果:PASS,0 errors
vitest run organizationAutoInstall.test.ts autoSyncService.test.ts
结果:2 文件、60 测试通过

手工验证

配套服务端已完成隔离 PostgreSQL 联调及 Console 浏览器页面验证;本 PR 的 Desktop 安装行为通过单元测试验证。

未执行的验证

未在正式环境实测桌面自动安装;未进行 Windows 实机验证。复用既有安装器,不改平台文件布局。

风险

风险分类

  • 无已知风险(N/A:涉及跨端协议)
  • SQLite / migration(N/A)
  • system prompt(N/A)
  • 协议兼容
  • 权限 / 安全 / 用户数据
  • 存量插件兼容(批准状态 / 指纹 / manifest 校验 / 安装布局 / 包格式)(N/A:未改插件)
  • 原生层 / fingerprint / OTA(N/A)
  • 跨平台差异(N/A:复用现有安装器)
  • 其他:N/A

影响与回滚

  • 影响范围:Skill Hub 自动同步。新增 GET /api/skills-hub/auto-install,校验分页游标与候选版本,限制分页数量。拉取或安装期间身份变化会取消;名单失败不清除忽略偏好。
  • 回滚 / 降级方式:先部署配套服务端再发布客户端。旧服务不支持新接口时继续原有产品同步;客户端回滚后不再消费企业名单,已安装技能保留。关闭策略不自动卸载。存量插件影响:无。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s)
  • UI 改动已在「UI 变化」注明引用的设计规范章节(N/A:无 UI 改动)
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档
  • 已确认测试结果或说明未执行原因

kirozeng and others added 30 commits September 8, 2026 16:55
review 指出占位符使合入后无法区分已批准裁决与 AI 草案。以可核验
事实补记:项目所有者以 PR makecindy#4072 作者身份提交并全程推进本裁决
(PR 描述载明「按所有者已定案方案;所有者 2026-09-07 圆角裁决」),
2026-09-08 指示补记本条并解除全部合并阻塞。PR 作者身份、描述原文
与所有者本人执行的合并操作互为佐证。

Signed-off-by: Kirozeng <kirozeng@gmail.com>
Signed-off-by: DavidShen <david@xd.com>
…道未启动 (makecindy#4102)

* fix(anthropic-compat-proxy): 回环端口 ECONNREFUSED 的 502 明确提示本地代理 / SSH 隧道未启动 (makecindy#4100)

自定义供应商指向本机回环地址(本地代理 / SSH 隧道)时,隧道没起来的错误被原样拼成
`502 upstream unreachable: connect ECONNREFUSED 127.0.0.1:18080`,harness 再按通用 502
解读成「服务端临时问题」,把用户引向远端服务或订阅排查。

新增 upstream-failure.ts:仅对「ECONNREFUSED + 回环地址」(含 AggregateError 内层)换成
可操作措辞——本机该端口没有监听,若是本地代理或 SSH 隧道请先启动再重试,不是远端服务或
订阅问题;并给稳定 code upstream_loopback_refused。经出站代理时措辞指向代理本身。其它
失败文案逐字不变;不改任何转发行为。

Signed-off-by: ficowang <fico@xd.com>

* fix(anthropic-compat-proxy): 出站代理 agent 保留底层 errno(cause),回环代理端口拒绝同样给出明确提示 (makecindy#4100)

review 反馈:TunnelingHttpsAgent(HTTPS 经 HTTP CONNECT)与 SOCKS5 agent 把连接错误包装成
纯文本 Error,code/address 丢失,回环代理端口没监听时仍落回通用 502。
- outbound-proxy.ts / socks5.ts 包装错误时带 { cause: err }
- upstream-failure.ts 分类器沿 Error.cause 与 AggregateError.errors 逐层查找
- 单测:cause 包装 / 二级包装识别,纯文本不猜;集成:关掉回环 HTTP CONNECT 代理端口、
  关掉回环 SOCKS5 代理端口 → 502 body 带 upstream_loopback_refused 与「outbound proxy at」。
反证:去掉 cause 保留后两条集成用例失败。

Signed-off-by: ficowang <fico@xd.com>

---------

Signed-off-by: ficowang <fico@xd.com>
Signed-off-by: xd-bobo <caojianbo@xd.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(desktop): 远程任务顶部仅保留异常提示

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(i18n): 清理已移除远程状态提示的翻译

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(desktop): clear Agent Island unread bot messages

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): refresh bot island session ownership

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): match bot history route explicitly

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): limit island read state to visible chat routes

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): report bot island visibility after ownership validation

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* docs(design): refresh inventory for bot island visibility hook

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* test(desktop): await bot visibility effects before refocus

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): let main accept validated bot focus acknowledgements

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
…dius-registration

docs(design): 按可见层与登记分配圆角并明确审查边界
Signed-off-by: xd-bobo <caojianbo@xd.com>
…es (makecindy#4109)

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>
fix(codex): 修复 fork 后子任务无法继续发送消息
Signed-off-by: DavidShen <david@xd.com>
同步 main(至 043eaec,makecindy#4072 已合入),解决与 makecindy#4076 的设计文档冲突:

- §5 采用 makecindy#4072 重写的「可见层 + 登记驱动」框架(Step 1 已登记形状 /
  Step 2 控件三档);makecindy#4064 的 usage data marks 例外已由框架内成员表吸收
- 本 PR 的交互几何登记改写为 Interaction constraints 内的
  「Registered interaction emphasis」条目,锚定 data mark 成员
  usage-heatmap-day / usage-token-bar;§4 指针与 cindy-design-system
  版本记录同步改引用
- §14.4 Press 行取 main 新措辞,Done 行保留对下方图表响应登记的指引
- design-inventory 人工区融合两边跟进记录,生成区由
  pnpm design:inventory 再生成并通过 check:design-inventory

Signed-off-by: Kirozeng <kirozeng@gmail.com>
* fix(mobile): 统一待发送与正式消息的附件布局

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): 稳定待发消息折叠并隔离正文手势

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): 将待发操作徽标定位到内容边界

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): 保留待发送相册图片的原生预览支持

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
…s-and-date-selection

fix(desktop): 优化用量历史图表配色、交互反馈与文字层级
fix(mobile): 修复 Android 听写输入文字重影
Signed-off-by: xd-bobo <caojianbo@xd.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>
Signed-off-by: xd-bobo <caojianbo@xd.com>
…ency

Signed-off-by: DavidShen <david@xd.com>
Signed-off-by: zqchris <chrisz83@gmail.com>
Signed-off-by: DavidShen <david@xd.com>
Signed-off-by: zqchris <chrisz83@gmail.com>
Signed-off-by: xd-bobo <caojianbo@xd.com>
dashhuang and others added 27 commits September 9, 2026 14:23
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
…ctions

Signed-off-by: xd-bobo <caojianbo@xd.com>
…esktop

feat(remote-desktop): 手机远程桌面、网络协商与断线恢复
…on-demand

perf(device-link): 按可见内容分页并按需读取工作详情
Signed-off-by: zqchris <chrisz83@gmail.com>
…agement

feat(skillhub): 支持本地技能启停、卸载与详情跳转
Signed-off-by: zqchris <chrisz83@gmail.com>
feat(browser): add per-start (unauthenticated) proxy server support
… 1M 入口 (makecindy#3883) (makecindy#4093)

Coding Plan 端点下 GLM-5.3 实际支持 1M 上下文(同一目录的 Pi 列表已声明 glm-5.3 为 1M,
提报人实测填 1000000 后与 CLI 直连一致),但两个 Coding Plan 预设的 Claude Code 列表没有
glm-5.3 条目,用户手填裸 id 时留空按 200K 保守默认运行。

与既有 glm-5.2[1m] 同形态补 glm-5.3[1m](GLM-5.3 (1M),contextWindow 1000000)独立条目;
不改动既有裸条目,不做 GLM-5.x 全局 1M。契约测试同时锁定 Pi 侧窗口来源与裸条目不变。

Signed-off-by: ficowang <fico@xd.com>
Signed-off-by: zqchris <chrisz83@gmail.com>
Signed-off-by: zqchris <chrisz83@gmail.com>
…dy#4142)

* fix(codex): repair subscription recovery and paginated forks

Signed-off-by: zqchris <chrisz83@gmail.com>

* fix(codex): reject fork boundaries based on visible turn counts

Signed-off-by: zqchris <chrisz83@gmail.com>

---------

Signed-off-by: zqchris <chrisz83@gmail.com>
* feat(design-system): DS-6 完成表单与确认组件复用

Signed-off-by: kirozeng <kirozeng@gmail.com>

* fix(desktop): 表单弹窗内的 Tip 提升到模态层之上

review P2:CustomProviderDialog / McpServerDialog 的密码显隐按钮与
行删除按钮的 Tip 经 Portal 渲染,内容层默认 z-[60] 低于手写模态的
z-[10000],悬停/聚焦时提示被宿主弹窗盖住。

- Input 为 secret 眼睛按钮新增 secretTipContentClassName 透传,两张
  表单的密钥/Token 输入传 z-[10001]
- 行删除 Tip 传 contentClassName="z-[10001]",沿用仓内既有惯例
  (SessionShareExportDialog / AccountSwitcherDialog 等)
- DESIGN.md Dialog & Modal 段回写规则,DS-6 证据文件补记录
- 回归测试:两张表单悬停触发后断言可见 tooltip 层含 z-[10001]

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(desktop): AddProviderWizard 密钥输入的 Tip 同步抬到模态层之上

review P1:向导内置供应商与预设 API Key 两个 SettingsTextInput secret
调用点同处 z-[10000] 遮罩内,Tip 默认 z-[60] 仍会被盖住;补传
secretTipContentClassName="z-[10001]"(与两张表单同批修复)。仓内
secret 调用点全景核查:两表单与向导均已抬层,ProvidersSection 的
ImageApiKeyRow 为设置页行内无模态遮罩,保持默认。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(desktop): MCP transport 改用共享分段控件并同步设计索引

review P2 ×2:
- McpServerDialog 的 transport 互斥选择从两个独立 Button + aria-pressed
  改用 SettingsSegmentedControl(DESIGN.md §4 Settings segmented
  controls),获得单一 Tab 停靠点、方向键、Home/End 与 RTL 键盘行为,
  不再自造第二套选中态视觉;测试同步改为 radiogroup/radio 断言。
- cindy-design-system.md「新贡献者从这里开始」第 3 条不再说 FormField
  与 Button loading 尚未提供,改指向上方 DS-6 表单贡献入口。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(desktop): 字段错误只在报错字段自身被编辑时清除

review P2:面板级 onChangeCapture 原先在任意后代输入变化时清空唯一的
fieldError——Base URL 无效时改名称或 API Key 会立即消失 URL 的错误
提示与 aria-invalid,尽管 URL 未修正。现仅在 event.target 即报错字段
本身时清除;其它字段的编辑保留提示直至再次保存重新校验。回归测试
覆盖「改其它字段错误保留 / 改报错字段错误清除」两分支。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(desktop): 非输入路径修正字段时同步清除过期错误

review P1:上一轮把错误清除收敛到「报错元素自身的 change」后,两个
非输入控件路径会让错误滞留到再次保存:

- 列表级错误(`${agent}:add-model`,模型列表为空,提示挂在「添加模型」
  按钮旁):点该按钮新增行并填写内容时,change 目标是新行输入而非按钮,
  提示不清。现在该 agent 模型行的输入变化同样视为对列表错误的修正。
- applyPreset 程序化替换名称/鉴权/全部 runtime:不触发任何输入 change,
  既有错误指向整体失效。函数内显式清除。

回归测试 ×2:删光模型行保存→重添行填写即清;应用预设即清并核对
名称已被程序化替换。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

---------

Signed-off-by: kirozeng <kirozeng@gmail.com>
Signed-off-by: Kirozeng <kirozeng@gmail.com>
* fix(models): apply context budgets across all three harnesses

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(models): restrict Pi window overrides to applied patches

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(context): retain verified route ceilings in history protection

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(codex): apply context defaults to SSH thread lifecycle

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(context): resolve settings for provider-less default routes

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(context): share implicit provider resolution with history guards

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(context): reload Pi gateway defaults and preserve SSH session resources

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(context): cap working budgets by verified maximum capacity

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(context): retain failed catalog reloads and continue the batch

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(context): protect explicit budgets and reconcile failed refreshes

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

---------

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>
Co-authored-by: Chris <4436110+zqchris@users.noreply.github.com>
* fix(mobile): 保持图片发送交接时的预览与布局连续

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): 避免消息回流时重复占位

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix: unify automation and settings model selection

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: guard scheduled Harness migration on partial databases

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: preserve project automation selection and reconcile Fast

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: persist template Harness selections for bound schedules

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: persist Fast-only edits for legacy bound automations

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: preserve scheduled model selections through recovery and editing

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: mark explicit mobile automation Harness choices

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: preserve scheduled model selection across remote versions and fresh runs

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: preserve scheduled provider and bound Harness baselines

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: clear Mobile Harness override when following bound model

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix: retain actual Harness when rebinding mobile schedules

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

---------

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>
Co-authored-by: Chris <4436110+zqchris@users.noreply.github.com>
…akecindy#4165)

* fix(scheduler): 换 agentKind 时丢弃上一引擎的模型路由,schedule_update 支持 null 清空

伙伴接管期把任务钉在 Codex 的 gpt-6-astra@openai 上,之后改回 Claude Code 时:
- schedule_update 的 model / providerId / effort / workingDir 只收 string,传 null 报
  INVALID_ARGS,陈旧路由清不掉;
- 引擎 update 只做浅合并,agentKind 变了仍带着旧引擎的 model / providerId;
- fire 时路由守卫对「没有任何来源提供该模型」的组合放行,runner 把绑定会话的
  凭证/模型切到这条跑不通的路由上,每轮以「upstream account cannot access this
  model」失败,会话 meta 也被写成 gpt-6-astra@openai。

修复:
- maker-scheduler updateUnlocked:patch.agentKind 与现有不同且未显式给
  model / providerId / effort 时,以带 key 的 undefined 清掉(storage 清列 NULL),
  任务回到新引擎默认路由;显式给了按调用方意图。
- lizi-mcps schedule_update:四个路由字段 .nullable(),null 翻成带 key 的
  undefined(与 targetSessionId / intervalMs 同约定)。

Signed-off-by: ficowang <fico@xd.com>

* fix(lizi-mcps): schedule_update 清 workingDir 时拒绝仍开 useWorktree 的任务,并回到 dialogue

codex review:useWorktree 以 workingDir 为基仓,只清目录会留下一条 fire 必失败的任务。
清目录时若任务仍开着 useWorktree 且本次未关,报 invalid request;清目录同时把
workspaceKind 归到 dialogue(与 create 缺省目录推断对称)。

Signed-off-by: ficowang <fico@xd.com>

* fix(maker-scheduler): 换 agentKind 时一并清除旧引擎的 fastMode

codex review:fastMode 属完整运行路由(harness + provider + model + effort + fastMode),
只清 model/providerId/effort 会让旧 Codex 任务的 Fast 开关潜伏到下次切回 Codex/Pi
时复活,且 MCP schedule_update 没有 fastMode 字段可清。未显式给则置 false。

Signed-off-by: ficowang <fico@xd.com>

---------

Signed-off-by: ficowang <fico@xd.com>
* fix(file-browser): 无 NUL 字节的 PDF 按二进制识别,进入既有 PdfPreview

`readFile()` 只用「前 4 KiB 含 NUL」判二进制,对象流未压缩、纯 ASCII 的
合法 PDF 整份文件可能没有 NUL,于是被当 UTF-8 文本回给渲染层,显示成
`%PDF-1.4 ... obj ... stream` 原始语法;渲染层的 PdfPreview 只在
kind: 'binary' + .pdf 扩展名时才走到。

在 readFile / writeFile 共用的二进制判定里增加 PDF 文件头识别:前 1 KiB
内出现 `%PDF-`(PDF 规范 / pdf.js 同口径)即判为 BINARY_FILE,本地与远程
(remote-file-service 同一 readFile)都按既有 binary 分支进入 PdfPreview,
编辑器同样拒绝覆写 PDF。不改渲染层。

Fixes makecindy#4158

Signed-off-by: ficowang <fico@xd.com>

* fix(file-browser): PDF 文件头判定只对 .pdf 生效,并 bump 远端 bundle 版本

review 反馈两点:
- 文件头判定不限扩展名会把开头引用 `%PDF-` 的 markdown / 日志 / 源码误判为
  二进制(非 .pdf 只会得到不可渲染占位、编辑器也拒绝保存)。渲染层的 PDF
  预览本来就以扩展名为准,故判定收窄到 `.pdf`(大小写不敏感)。
- remote-file-service 的 daemon 只在 schema 或 bundle 版本变化时重推,不 bump
  FILE_SERVICE_BUNDLE_VERSION 则已装 0.2.3 的 SSH 主机继续跑旧的仅 NUL 探测;
  随本次 scanner 行为变更 bump 到 0.2.4。

Refs makecindy#4158

Signed-off-by: ficowang <fico@xd.com>

* ci: 重触发 client-ci

Signed-off-by: ficowang <fico@xd.com>

---------

Signed-off-by: ficowang <fico@xd.com>
* fix(mobile): 首页自动恢复时显示不可点击的进度

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): 按实际恢复状态显示首页进度

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): keep home recovery row visible during device probe

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): preserve actionable errors during multi-device recovery

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): classify home errors before adding device names

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): associate home recovery with failing device identity

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): keep recovery progress through reseed completion

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): float connection notices after a one-second delay

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): retain recovery completion for visible connection notices

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
feat(bots): 统一持久授权卡与授权完成续接
Signed-off-by: qianbo <qianbo@xindong.com>
@qbqian qbqian closed this Sep 21, 2026
@greptile-apps

greptile-apps Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 2/5

此 PR 暂不适合合并,因为组织接口挂起会阻塞原有产品同步,同账号快速重登可能漏拉策略,同名企业技能也无法接管已有 market 自动安装。

Findings

  1. P1 组织请求会阻塞产品同步 ▶
  2. P1 同账号重登可能漏拉策略 ▶
  3. P1 企业技能无法接管同名安装 ▶
Fix with agent prompt
### Issue 1
apps/desktop/src/main/skillhub/autoSyncService.ts:511-513
组织名单请求没有超时,却与产品配置一起被 `Promise.allSettled` 等待。`skillhubApiFetch` 未传 `timeoutMs`,下层默认不创建超时控制器;如果旧服务或网络连接一直不返回,组织请求就不会结束,已经成功取得的产品配置也无法继续处理,整次自动同步会永久停在这里。这违背了“新接口失败时继续原有产品同步”的降级要求。请为组织分页请求设置有限超时,或将其与产品同步解耦。

### Issue 2
apps/desktop/src/main/skillhub/autoSyncService.ts:158
清空 `completedUserId` 仍不能保证同账号重新登录会重拉策略。如果旧同步尚未结束,重新登录触发的 `runOnceAfterLogin()` 会因 `inFlightUserId` 与当前用户相同而直接复用正在取消的旧 Promise;旧任务结束后不会为新会话排队同步,因此这次企业策略拉取会被静默跳过。应记录取消后的重跑请求,或像不同账号分支一样在旧任务结束后重新调用同步。

### Issue 3
apps/desktop/src/main/skillhub/autoSyncService.ts:518
企业同名项覆盖产品项后,已有的 market 自动安装无法切换到 team 来源。现有安装按 `catalogScope` 建索引,因此 `market:<slug>` 不会被新的 `team:<slug>` 识别为可更新项;它又因 `autoSynced` 被排除在冲突保护表之外。随后代码会对同一个物理目录执行一次不带 `force` 的全新安装,安装器返回 `CONFLICT_USER_OWNED` 并被静默跳过。结果是用户一直保留旧 market 技能,而企业优先的 team 版本无法安装。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary

此 PR 在桌面端 Skill Hub 自动同步中加入企业部门策略名单:登录后分页读取组织候选,将企业同名技能置于产品默认项之前,并复用现有市场同步、安装、校验、忽略偏好和取消清理流程。

  • 新增组织自动安装响应及分页游标的严格校验。
  • 登出时重置自动同步完成标记,意图支持同账号重新登录后重新拉取。
  • 当前仍存在组织请求无超时、同账号快速重登漏跑,以及跨 catalog scope 的同名技能无法接管三个阻塞问题。
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Login[企业成员登录或切换身份] --> Product[获取产品自动同步配置]
  Login --> Org[分页获取组织部门策略]
  Product --> Merge[按技能名称合并]
  Org --> Merge
  Merge -->|企业同名项优先| Ignore[应用用户忽略偏好]
  Ignore --> Sync[按 catalog scope 同步 Skill Hub]
  Sync --> Registry[匹配本地安装记录]
  Registry --> Install[复用下载、校验与安装器]
  Install --> Links[更新注册表与共享技能链接]
Loading

Reviews (1) · Last reviewed commit: "feat(skillhub): install organization ski..."

Comment on lines +511 to +513
const [product, organization] = await Promise.allSettled([
fetchProductAutoSyncConfig(), fetchOrganizationAutoInstallSkills(),
]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 组织请求会阻塞产品同步

组织名单请求没有超时,却与产品配置一起被 Promise.allSettled 等待。skillhubApiFetch 未传 timeoutMs,下层默认不创建超时控制器;如果旧服务或网络连接一直不返回,组织请求就不会结束,已经成功取得的产品配置也无法继续处理,整次自动同步会永久停在这里。这违背了“新接口失败时继续原有产品同步”的降级要求。请为组织分页请求设置有限超时,或将其与产品同步解耦。

Knowledge Base Used: Desktop runtime processes

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/main/skillhub/autoSyncService.ts
Line: 511-513

Comment:
**组织请求会阻塞产品同步**

组织名单请求没有超时,却与产品配置一起被 `Promise.allSettled` 等待。`skillhubApiFetch` 未传 `timeoutMs`,下层默认不创建超时控制器;如果旧服务或网络连接一直不返回,组织请求就不会结束,已经成功取得的产品配置也无法继续处理,整次自动同步会永久停在这里。这违背了“新接口失败时继续原有产品同步”的降级要求。请为组织分页请求设置有限超时,或将其与产品同步解耦。

**Knowledge Base Used:** [Desktop runtime processes](https://app.greptile.com/xindong/-/custom-context/knowledge-base/xindong/cindy/-/docs/desktop-runtime-processes.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@@ -146,6 +155,7 @@ export class SkillhubAutoSyncService {
}

cancelInFlight(): void {
this.completedUserId = null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 同账号重登可能漏拉策略

清空 completedUserId 仍不能保证同账号重新登录会重拉策略。如果旧同步尚未结束,重新登录触发的 runOnceAfterLogin() 会因 inFlightUserId 与当前用户相同而直接复用正在取消的旧 Promise;旧任务结束后不会为新会话排队同步,因此这次企业策略拉取会被静默跳过。应记录取消后的重跑请求,或像不同账号分支一样在旧任务结束后重新调用同步。

Knowledge Base Used:

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/main/skillhub/autoSyncService.ts
Line: 158

Comment:
**同账号重登可能漏拉策略**

清空 `completedUserId` 仍不能保证同账号重新登录会重拉策略。如果旧同步尚未结束,重新登录触发的 `runOnceAfterLogin()` 会因 `inFlightUserId` 与当前用户相同而直接复用正在取消的旧 Promise;旧任务结束后不会为新会话排队同步,因此这次企业策略拉取会被静默跳过。应记录取消后的重跑请求,或像不同账号分支一样在旧任务结束后重新调用同步。

**Knowledge Base Used:**
- [Authentication and identity](https://app.greptile.com/xindong/-/custom-context/knowledge-base/xindong/cindy/-/docs/authentication-and-identity.md)
- [Desktop runtime processes](https://app.greptile.com/xindong/-/custom-context/knowledge-base/xindong/cindy/-/docs/desktop-runtime-processes.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

: { skills: defaultAutoSyncSkills(), usingFallbackConfig: true };
const skills = new Map(productConfig.skills.map((skill) => [skill.name, skill]));
if (organization.status === 'fulfilled') {
for (const skill of organization.value) skills.set(skill.name, skill);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 企业技能无法接管同名安装

企业同名项覆盖产品项后,已有的 market 自动安装无法切换到 team 来源。现有安装按 catalogScope 建索引,因此 market:<slug> 不会被新的 team:<slug> 识别为可更新项;它又因 autoSynced 被排除在冲突保护表之外。随后代码会对同一个物理目录执行一次不带 force 的全新安装,安装器返回 CONFLICT_USER_OWNED 并被静默跳过。结果是用户一直保留旧 market 技能,而企业优先的 team 版本无法安装。

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/main/skillhub/autoSyncService.ts
Line: 518

Comment:
**企业技能无法接管同名安装**

企业同名项覆盖产品项后,已有的 market 自动安装无法切换到 team 来源。现有安装按 `catalogScope` 建索引,因此 `market:<slug>` 不会被新的 `team:<slug>` 识别为可更新项;它又因 `autoSynced` 被排除在冲突保护表之外。随后代码会对同一个物理目录执行一次不带 `force` 的全新安装,安装器返回 `CONFLICT_USER_OWNED` 并被静默跳过。结果是用户一直保留旧 market 技能,而企业优先的 team 版本无法安装。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.