@inventec/dsh-copilot-auth 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,10 +18,10 @@
18
18
 
19
19
  - 🔐 **设备码登录/注销**:网页 + user code,免 API Token;SSO 组织授权友好
20
20
  - 🧩 **预置提供方路由**:安装即出现在 Models 页,无需手动配置
21
- - 📦 **模型目录自动同步**:登录成功或插件启动时,自动把账号全部可用模型写入模型目录,开箱即用
21
+ - 📦 **模型目录兜底填充**:登录成功时,若 GitHub Copilot 路由还没有模型目录,自动填入账号全部可用模型;你已精简/定制过的目录**永不覆盖**(重启、重新登录均不重置)
22
22
  - 🌐 **中/英双语界面**:跟随 DSH 语言设置自动切换
23
23
  - 🔑 **凭据安全托管**:存入 DSH 内置凭据库(文件强制 0600 权限),Copilot 临时 token 到期自动刷新
24
- - 🧪 **测试与 CI**:13 条单元测试;GitHub Actions 构建测试 + tag 触发自动发布(provenance)
24
+ - 🧪 **测试与 CI**:15 条单元测试;GitHub Actions 构建测试 + tag 触发自动发布(provenance)
25
25
 
26
26
  ## 前置要求
27
27
 
@@ -48,13 +48,13 @@ dsh plugin --profile web add @inventec/dsh-copilot-auth
48
48
  2. 点「登录」,页面显示一串**代码**(user code,可一键复制)与 `https://github.com/login/device` 链接
49
49
  3. 新标签打开链接,用公司 GitHub 账号输入代码
50
50
  4. **SSO 用户必须对组织点 Authorize**(授权页会出现该步骤,跳过则登录不生效)
51
- 5. 回到设置页看到「已登录」即成功;模型目录此时已自动同步
51
+ 5. 回到设置页看到「已登录」即成功;首次登录会自动填充模型目录(已定制过的目录不会被覆盖)
52
52
 
53
53
  > 设备码有时效(约 15 分钟),请拿到代码后尽快完成授权。若超时,页面会显示失败原因,重新点「登录」即可。
54
54
 
55
55
  ## 使用
56
56
 
57
- - Models 页选择 `GitHub Copilot` 路由,模型目录已在登录/启动时自动同步账号全部可用模型,无需手动「添加模型」
57
+ - Models 页选择 `GitHub Copilot` 路由,模型目录已在首次登录时自动填充账号全部可用模型,无需手动「添加模型」;之后可自由增删精简,重启 / 重新登录都不会重置你的列表
58
58
  - **配额说明**:base 模型(GPT-4o/4.1 一类)不耗 premium requests;premium 模型(Claude、Gemini、o 系列等)每次调用消耗月度配额。日常建议 base 档,premium 模型按需手动选
59
59
 
60
60
  ## 注销与卸载
@@ -69,13 +69,13 @@ dsh plugin --profile web add @inventec/dsh-copilot-auth
69
69
  2. 注册本插件(host 侧在 webserver 上开 4 条本地路由:`/copilot-auth/start|state|status|logout`,跨站 Origin 拒绝)
70
70
  3. 以 settings base 层预置 `github-copilot` 路由(用户 `settings.yaml` 可逐字段覆盖)
71
71
  - 登录走 GitHub 设备码流;凭据存 `~/.dsh/.credentials.yaml`(强制 600 权限)的 `llm-pi-ai/github-copilot` 记录,Copilot 临时 token 到期自动刷新
72
- - **模型目录自动同步**:登录成功或插件启动时,取「账号可用模型 ∩ pi-ai 内置目录」写入该路由的模型目录,同步失败经 `/copilot-auth/status` 的 `syncError` 字段暴露
72
+ - **模型目录兜底填充**:登录成功时,取「账号可用模型 ∩ pi-ai 内置目录」写入该路由的模型目录——仅当该路由尚未配置 `models` 且无 `modelOverrides` 时写入;目录已存在(含空列表)一律让路,插件启动/重启也绝不触碰用户 settings。同步失败经 `/copilot-auth/status` 的 `syncError` 字段暴露
73
73
 
74
74
  ## 开发
75
75
 
76
76
  ```bash
77
77
  npm install
78
- npm test # node:test:patch 结构 + host 行为共 13 条
78
+ npm test # node:test:patch 结构 + host 行为共 15 条
79
79
  npm run build # esbuild 打包 client 到 lib/client.js(__ModuleLoader__ 信封)
80
80
  npm pack --dry-run
81
81
  ```
@@ -102,8 +102,8 @@ npm pack --dry-run
102
102
  - `settings.yaml` 的 `llm-pi-ai:` 节只能稀疏覆盖字段,无法删除预置路由本身(移除须卸载本插件)
103
103
  - 路由前缀 `/copilot-auth` 两侧硬编码,不可配置
104
104
  - DSH rc 版本耦合:实测 `0.1.2-rc.1`,peer 仅 `@deepseek-ai/cordis@^4.0.2`
105
- - 每次登录成功或插件启动,会用账号最新发现的可用模型**全量覆盖** GitHub Copilot 路由的模型目录——对目录的手工定制(如模型显示名)会在下次同步时被重置为裸模型 id
106
- - 模型目录只写入 pi-ai 内置目录已描述的模型:目录快照外的新模型暂不写入(catalog 路由的校验要求模型协议可解析),pi-ai 升级目录后会随同步自动出现
105
+ - 模型目录只在「尚不存在」时由登录成功兜底填充一次,填充后归用户所有:账号新增的模型不会自动出现,需在 Models 页手动添加,或删掉 `settings.yaml` 里该路由的 `models` 列表后**重新登录**重新填充全量(插件启动不触发填充)
106
+ - 模型目录只写入 pi-ai 内置目录已描述的模型:目录快照外的新模型暂不写入(catalog 路由的校验要求模型协议可解析),pi-ai 升级目录后重新登录即可纳入
107
107
 
108
108
  ## 维护者发布
109
109
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inventec/dsh-copilot-auth",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Device-code sign-in UI and preconfigured route for the built-in GitHub Copilot provider of DeepSeek Harness (DSH)",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/host.mjs CHANGED
@@ -31,17 +31,39 @@ function readCatalogModelIds() {
31
31
  return null;
32
32
  }
33
33
 
34
- // 登录成功(或挂载时已登录)后,把账号可用模型写入用户 settings 的模型目录
35
- // (turnkey,用户新增需求 2026-09-04)。
34
+ // 登录成功后,把账号可用模型写入用户 settings 的模型目录
35
+ // (turnkey,用户新增需求 2026-09-04;目录保护,用户反馈 2026-09-05)。
36
36
  // 可写集合取交集:凭据 payload.availableModelIds(账号可用)∩ 内置目录已描述。
37
37
  // 目录快照外的 id 必须排除——catalog 路由校验要求模型的 wire 协议可解析
38
38
  // (api = 路由设置 ?? 目录条目 ?? 路由共享协议),目录外 id 三者皆空会被整体
39
39
  // 拒绝;pi-ai 目录更新后它们会自然进入可写集合。
40
- // 注意:每次登录/挂载会用最新列表覆盖该目录,手工定制会被重置。
40
+ // 目录保护(2026-09-05):该路由已配置模型目录(models 键存在,含空列表)或
41
+ // modelOverrides 时视作用户所有,同步直接让路——用户在 Models 页精简过的目录
42
+ // 不会被重置。只有目录尚不存在时才填充一次;想重置全量,删掉 settings.yaml
43
+ // 里该路由的 models 列表后重新登录即可。
44
+
45
+ // 读取解析后的 github-copilot 路由配置(base 层 + 用户层的最终值)。
46
+ // settings 未注入 / 未注册 / 读取失败一律按空处理——回退到可写入分支,
47
+ // 保持既有 turnkey 行为不变。
48
+ function readConfiguredRoute(ctx) {
49
+ try {
50
+ const section = ctx.settings?.get?.("llm-pi-ai");
51
+ return section?.providers?.["github-copilot"] ?? {};
52
+ } catch {
53
+ return {};
54
+ }
55
+ }
56
+
41
57
  async function syncAvailableModels(ctx) {
42
58
  const record = await ctx.credentials.readRecord(CREDENTIAL_KEY);
43
59
  const available = record?.payload?.availableModelIds;
44
60
  if (!Array.isArray(available) || available.length === 0) return;
61
+ const route = readConfiguredRoute(ctx);
62
+ const modelsConfigured = route.models !== undefined && route.models !== null;
63
+ const overridesConfigured = !!route.modelOverrides
64
+ && typeof route.modelOverrides === "object"
65
+ && Object.keys(route.modelOverrides).length > 0;
66
+ if (modelsConfigured || overridesConfigured) return;
45
67
  const catalog = readCatalogModelIds();
46
68
  let ids;
47
69
  if (catalog) {
@@ -90,13 +112,13 @@ export function apply(ctx) {
90
112
  let attempt = emptyState();
91
113
  let lastSyncError;
92
114
 
93
- // 挂载时若已登录,同步一次模型目录(凭据持久化在本地,模型列表随账号刷新)。
115
+ // 模型目录同步只在登录成功后执行(2026-09-05 对齐结论:挂载不再同步)。
116
+ // 插件启动/重启绝不触碰用户 settings,用户精简过的目录不会被动重置。
94
117
  // 失败不静默:错误经 /status 的 syncError 字段暴露,便于诊断。
95
118
  const sync = () => syncAvailableModels(ctx).then(() => { lastSyncError = undefined; }).catch((err) => {
96
119
  lastSyncError = String(err?.message ?? err);
97
120
  ctx.logger?.warn?.("copilot-auth: model sync failed: %s", lastSyncError);
98
121
  });
99
- void sync();
100
122
 
101
123
  ctx.webServer.register({
102
124
  kind: "exact",