@eduwork/dsh-oidc 0.2.3 → 0.2.4

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.
Files changed (38) hide show
  1. package/CHANGELOG.en.md +6 -38
  2. package/CHANGELOG.md +7 -39
  3. package/CODE_OF_CONDUCT.en.md +1 -1
  4. package/CODE_OF_CONDUCT.md +1 -1
  5. package/CONTRIBUTING.en.md +1 -1
  6. package/CONTRIBUTING.md +1 -1
  7. package/README.md +19 -16
  8. package/{README.en.md → README_EN.md} +15 -8
  9. package/SECURITY.en.md +1 -1
  10. package/SECURITY.md +1 -1
  11. package/SUPPORT.en.md +1 -1
  12. package/SUPPORT.md +1 -1
  13. package/docs/EDUWORK-MIGRATION.en.md +2 -8
  14. package/docs/EDUWORK-MIGRATION.md +2 -8
  15. package/docs/compatibility.en.md +3 -25
  16. package/docs/compatibility.md +3 -25
  17. package/docs/desktop-host.en.md +55 -55
  18. package/docs/desktop-host.md +64 -64
  19. package/docs/development.en.md +44 -44
  20. package/docs/development.md +4 -4
  21. package/docs/dsh-integration.en.md +2 -2
  22. package/docs/dsh-integration.md +2 -2
  23. package/docs/getting-started.en.md +4 -4
  24. package/docs/getting-started.md +4 -4
  25. package/docs/open-integration.en.md +73 -0
  26. package/docs/open-integration.md +73 -0
  27. package/docs/release-checklist.en.md +7 -61
  28. package/docs/release-checklist.md +7 -61
  29. package/docs/server-integration-contract.en.md +4 -2
  30. package/docs/server-integration-contract.md +5 -3
  31. package/examples/enterprise-profile.example.json +78 -78
  32. package/examples/identity-only.example.json +1 -1
  33. package/examples/resources-discovery.example.json +1 -1
  34. package/lib/provider/transforms.js +0 -1
  35. package/lib/typert.host.js +38 -38
  36. package/lib/typert.remote-client.js +37 -37
  37. package/package.json +6 -5
  38. package/schema/enterprise-profile.v1alpha1.schema.json +193 -193
@@ -0,0 +1,73 @@
1
+ # 开放身份与模型接入倡议
2
+
3
+ **简体中文** | [English](open-integration.en.md)
4
+
5
+ **让机构的账号与模型服务,被更多 AI 客户端复用。**
6
+
7
+ 学校和企业已经有自己的账号体系,也在建设模型服务。用户希望用熟悉的账号登录,在自己选择的 AI 客户端里开始工作。我们希望服务端实现一套公开接口后,能够服务多个遵循同一约定的客户端;客户端新增一个机构时,也能主要通过配置完成接入。
8
+
9
+ EduWork 从 `dsh-oidc` 开始实践这件事,并邀请身份平台、模型网关、客户端和插件作者一起完善这套约定。
10
+
11
+ > 状态:这是一份面向社区的倡议。配套的 RFC EW-IDENTITY-1 描述已有实现的接口,后续协议演进开放讨论;它不是 DSH 官方、OpenID Foundation 或 IETF 发布的标准,也不代表其他客户端已经兼容。
12
+
13
+ ## 用户应当得到什么
14
+
15
+ 用户选择机构,在系统浏览器完成登录,授权并在必要时确认创建模型凭据。客户端随后加载机构提供的模型,用户即可开始任务。个人 API Key 和其他模型仍然可以使用。
16
+
17
+ ```mermaid
18
+ flowchart LR
19
+ A[选择机构] --> B[浏览器登录与授权]
20
+ B --> C[按需确认并领取模型凭据]
21
+ C --> D[加载可用模型]
22
+ D --> E[开始工作]
23
+ ```
24
+
25
+ 这是托管模型模式的目标体验。只需登录身份的机构可以从纯 OIDC 模式开始,用户自行配置模型;不必先建设模型凭据服务。
26
+
27
+ | 参与者 | 我们希望共同减少的工作 |
28
+ | --- | --- |
29
+ | 使用者 | 反复复制 Key、查找接口地址和填写模型参数。 |
30
+ | 学校与企业 | 为每个客户端重复编写身份和模型接入逻辑。 |
31
+ | 客户端作者 | 为每个机构维护一套专有登录、凭据和模型适配。 |
32
+ | 插件作者 | 在工具插件里再次实现登录或另存一份用户凭据。 |
33
+
34
+ ## 共同约定什么
35
+
36
+ 1. **身份遵循已有标准。** 使用 OpenID Connect 与适合本机公共客户端的 PKCE 登录流程,用户身份以经过验证的 OIDC 结果为准。资源服务负责模型授权,不替换身份来源。
37
+ 2. **模型接入有公开契约。** 明确凭据状态、申请、读取、轮换和模型目录的请求与响应。OIDC 负责身份;模型凭据与目录使用另外定义的资源接口,不能把完成登录等同于完成模型接入。
38
+ 3. **机构配置保持为数据。** 品牌、受信任端点和模型策略由本地配置声明;配置不能下发脚本、安装工具或替换执行模块。需要新增行为时,通过显式安装的插件接入。
39
+ 4. **保留用户选择和机构边界。** 机构模型与个人模型可以共存。公共接入不要求某所学校、某个桌面壳、配额系统或活跃心跳;机构专属能力按需扩展。
40
+ 5. **兼容性可以核验。** 字段、错误、版本和迁移方式写入规范、OpenAPI、示例与测试。协议变化遵循[兼容性策略](compatibility.md),不让客户端靠猜测完成接入。
41
+
42
+ ## 从现有实现开始共建
43
+
44
+ 协议和客户端实现随源码提供,可以据此开展服务端接入及客户端互操作验证。
45
+
46
+ | 部分 | 当前范围 | 阅读入口 |
47
+ | --- | --- | --- |
48
+ | 身份接入 | OIDC Discovery、Authorization Code + PKCE、Token、JWKS、UserInfo;支持纯身份模式。 | [OIDC 兼容要求](oidc-interoperability.md) |
49
+ | 托管凭据 | Bootstrap,以及 provision、resolve、renew 生命周期接口。 | [服务端完整接口与请求示例](server-integration-contract.md) · [Key Binding OpenAPI](../protocol/openapi.yaml) |
50
+ | 模型目录 | 可信配置中的静态目录,或显式启用的 `/models` 自动发现。 | [客户端模式与模型发现](public-resource-protocol.md) |
51
+ | DSH 客户端 | 独立 npm 包 `@eduwork/dsh-oidc`,由 EduWork 使用,也供其他兼容 DSH 宿主集成。 | [模块说明](../README.md) · [桌面宿主接入](desktop-host.md) |
52
+ | 机构扩展 | 配置、技能和插件组合,复用公共接入能力。 | [EduWork@ECNU 示例](https://github.com/ecnu/EduWork-ECNU) |
53
+
54
+ 服务端需要按所选模式提供相应接口;本仓库不包含可直接部署的 OIDC 或 Key Binding 服务端。现有客户端都是单机应用,本机 Web 用于验证,不提供多人共享的登录会话服务。
55
+
56
+ 远程机构配置发现、远端模型 Key 注销等内容仍在 RFC 的可选扩展提案中;配额和活跃心跳继续由机构扩展处理。文档中的提案和能力声明不会自动启用未实现的功能。模型网关采用本文档规定的 OpenAI Chat Completions 兼容接口,其他模型及媒体接口须按各自契约另行核验。
57
+
58
+ 其他客户端可以独立实现这些网络接口,不必采用 EduWork 的界面或桌面壳。能否互通,以双方声明的协议版本和实际联调结果为准。
59
+
60
+ ## 你可以从哪里参与
61
+
62
+ - **身份或模型平台开发者**:从[服务端接口规范](server-integration-contract.md)和[接入指南](getting-started.md)开始,提供经过脱敏的接入示例,反馈现有平台难以满足的约束。
63
+ - **客户端作者**:尝试复用 `dsh-oidc`,或独立实现网络协议;验证登录、凭据生命周期、模型目录及错误处理,分享注明版本的互操作结果。
64
+ - **插件作者**:通过[账户扩展接口](account-extensions.md)接入机构能力,优先复用宿主授权传输与账户状态。
65
+ - **规范贡献者**:在 [EduWork Issues](https://github.com/ecnu/EduWork/issues) 描述场景与兼容性问题;修改接口前先讨论设计,并按[贡献指南](../CONTRIBUTING.md)同步规范、机器契约、示例和必要测试。
66
+
67
+ 我们欢迎不同机构、不同客户端的实现参与。示例请使用占位配置和合成数据;认证漏洞与凭据问题通过[安全报告入口](../SECURITY.md)反馈。
68
+
69
+ ## 与 DSH 插件生态的关系
70
+
71
+ [DSH Desktop 的插件生态倡议](https://github.com/anywhere-labs/dsh-desktop/blob/master/docs/plugin-ecosystem.md)推动插件之间的组合与协作。我们认同这个方向,希望在身份与模型接入这一环节提供可实现、可讨论的公共接口。这两份倡议各自独立;引用并不表示对方已经采纳本协议。
72
+
73
+ 下一步希望共同验证的是:同一个机构服务能否通过清晰的协议和少量配置,接入更多客户端,并在升级后继续工作。
@@ -1,65 +1,11 @@
1
- # Public release checklist
1
+ # Package release checklist
2
2
 
3
3
  [简体中文](release-checklist.md) | **English**
4
4
 
5
- Record the maintainer, date, command-output links, and exceptions in the release pull request or release record. An applicable but incomplete security, compatibility, or supply-chain item blocks public release; the current maintainer records why an item is not applicable. During the initial single-maintainer phase, do not invent a second human approval: independent technical review, CI/focused regression evidence, and an explicit maintainer decision form the release basis.
5
+ Source and publication are maintained in EduWork. Follow [the shared package workflow](https://github.com/ecnu/EduWork/blob/main/docs/PACKAGES_EN.md); this package retains its independent npm name and SemVer.
6
6
 
7
- This independent stable version is `0.2.0`; the manifest is updated while dev.7 runtime sources and historical frozen artifacts remain unchanged. The repository locks registry-published DSH RC.1 dependencies. npm authorization does not authorize a GitHub push or Release; record actual publication outcomes separately. Local Windows checks must not be reported as completed Linux/Node matrix or institution deployment acceptance.
8
-
9
- ## Authority and project metadata
10
-
11
- - [ ] Repository owner confirmed public release and the MIT license.
12
- - [ ] The `freedomkk-qfeng/dsh-oidc` remote and ownership of the `@eduwork/dsh-oidc` npm package are confirmed.
13
- - [ ] Maintainer, security, and release-manager rosters recorded, accurately stating whether backups and a recovery path currently exist.
14
- - [ ] GitHub Private Vulnerability Reporting is enabled and its entry point has been checked without sensitive data.
15
- - [ ] ECNU/ChatECNU name, example text, colors, and trademark notice approved.
16
-
17
- ## Source and protocol review
18
-
19
- - [ ] Independent technical review completed for OIDC, Key Binding, model-gateway invariants, Profile validation, Provider credentials, and the native boundary; the maintainer accepted findings and exceptions in the release record.
20
- - [ ] Enterprise Profile JSON Schema matches runtime behavior.
21
- - [ ] OpenAPI matches normative Key Binding prose and implementation.
22
- - [ ] OIDC standard references and limitations are current.
23
- - [ ] No unnecessary local reimplementation of a supported DSH capability.
24
- - [ ] Compatibility matrix and changelog updated.
25
-
26
- ## Security and privacy
27
-
28
- - [ ] Threat model reviewed for actual topology.
29
- - [ ] Single-user Credential Provider, Profile ownership and shared-key-slot boundaries verified.
30
- - [ ] OIDC success and negative conformance suite passed against staging.
31
- - [ ] Cross-user/tenant Key Binding authorization tests passed.
32
- - [ ] API-key expiry, rotation, suspension, and remote revocation behavior tested.
33
- - [ ] Confirmed the WebServer listens only on `127.0.0.1` and no reverse proxy, port forward, or tunnel exposes the local session to other users.
34
- - [ ] Remote TLS, CSP, cookies, CSRF, egress, clock, logging, APM, and backups reviewed.
35
- - [ ] Privacy/legal review completed for identity and audit data.
36
- - [ ] Secret scans of both the current tree and publishable Git history found no production hosts, IDs, keys, logs, personal paths, or personal data.
37
-
38
- ## Supply chain
39
-
40
- - [ ] Clean `npm ci` and `npm run check` completed on Windows and Linux with Node 22 and 24.
41
- - [ ] `npm audit` and static analysis reviewed (not merely run).
42
- - [ ] Every direct/transitive license and exception reviewed.
43
- - [ ] npm install scripts reviewed; unexpected scripts denied.
44
- - [ ] Lockfile diff reviewed and dependency versions pinned as intended.
45
- - [ ] GitHub Actions pinned to immutable commits before public release.
46
-
47
- ## Product acceptance
48
-
49
- - [ ] Plain Web DSH: login, callback, display name/sub fallback, provision, model call, refresh, check, logout.
50
- - [ ] Desktop backend: shared Wails/Electron flow, cancellation and model defaults verified; validate any maintained legacy native bridge separately.
51
- - [ ] Native multimodal and text-only + image-transform routes tested.
52
- - [ ] Unsupported reasoning-effort model still runs with thinking enabled.
53
- - [ ] Failure UX tested: Discovery, token, UserInfo, bootstrap, policy denial, model key, expiry, network timeout.
54
-
55
- ## Build and publish
56
-
57
- - [ ] `npm run check` passes from a clean checkout.
58
- - [ ] `npm pack --dry-run` and extracted tarball reviewed; only intended files included.
59
- - [ ] Source maps contain no private absolute paths or secrets.
60
- - [ ] Tag/release notes/checksums prepared.
61
- - [ ] The actual publication path is recorded: until a Trusted Publisher is configured, run the manual workflow only with `publish=false`; the first stable release may use the maintainer's npm CLI plus browser 2FA to publish the frozen tarball, explicitly without claiming provenance.
62
- - [ ] When using Trusted Publishing, the Publisher is bound to repository `freedomkk-qfeng/dsh-oidc`, workflow `release.yml`, and environment `npm`; the job uses pinned npm CLI `11.6.2`, and this commit's Windows/Linux × Node 22/24 CI is successful.
63
- - [ ] Inputs and artifacts are verified for the selected path: the CLI path checks frozen tarball identity, SHA-256, 2FA, and registry results; the Trusted path additionally checks the existing `v<package version>` tag, commit, workflow artifact, and an actual npm provenance attestation.
64
- - [ ] Publishing credentials were not written to the repository, logs, or deliverables, and the release does not claim Trusted Publishing before it is configured.
65
- - [ ] Rollback/deprecation and vulnerability-notification plan confirmed.
7
+ - Review the changed behavior, version, changelog, compatibility and package lock.
8
+ - Run npm ci and npm run check in this package directory, plus relevant real Host or browser acceptance when behavior changes. Use synthetic test data.
9
+ - Inspect the actual tarball, public documentation, licenses and exported entries. Never ship credentials or user data.
10
+ - Select this package in packages-release.yml; publish=false only produces artifacts. Explicit publication runs from the matching package version tag, using the npm environment and this repository's trusted publisher.
11
+ - Verify registry bytes before updating a product lock. Publishing npm does not publish a desktop Release or deploy a client.
@@ -1,65 +1,11 @@
1
- # 公开发布检查表
1
+ # 包发布检查表
2
2
 
3
3
  **简体中文** | [English](release-checklist.en.md)
4
4
 
5
- 在发布 Pull Request 或发行记录中记录维护者、日期、命令输出链接和例外说明。适用但未完成的安全、兼容或供应链项目会阻止公开发布;不适用项由当前维护者记录原因。项目处于初期单一维护者阶段时,不虚构第二位人工审批,以独立技术复核、CI/专项回归证据和维护者明确决定作为发布依据。
5
+ 源码与发布入口统一在 EduWork;遵循[公共包发布流程](https://github.com/ecnu/EduWork/blob/main/docs/PACKAGES.md),本包保留独立的 npm 名称和 SemVer。
6
6
 
7
- 本次独立稳定版本为 `0.2.0`;manifest 已更新,dev.7 的运行源码与历史冻结产物不变。仓库锁使用 npm 可安装的 DSH RC.1 基线。npm 发布授权不等于 GitHub 推送或 Release 授权,按实际执行结果记录。本机 Windows 检查不能冒充 Linux/Node 矩阵或机构真实部署验收。
8
-
9
- ## 授权与项目元数据
10
-
11
- - [ ] 仓库所有者确认公开发布和 MIT 许可证。
12
- - [ ] `freedomkk-qfeng/dsh-oidc` 远程仓库位置和 `@eduwork/dsh-oidc` npm 包所有权均已确认。
13
- - [ ] 维护者、安全维护者和发布经理名单已记录,并如实说明当前是否存在替补及恢复路径。
14
- - [ ] GitHub Private Vulnerability Reporting 已启用并完成一次无敏感数据的入口检查。
15
- - [ ] ECNU/ChatECNU 名称、示例文字、颜色和商标声明获批。
16
-
17
- ## 源码与协议审查
18
-
19
- - [ ] OIDC、Key Binding、模型网关一致性、Profile 校验、Provider 凭据和 native 边界已完成独立技术复核,维护者已在发布记录中接受结论和例外。
20
- - [ ] Enterprise Profile JSON Schema 与运行行为一致。
21
- - [ ] OpenAPI 与 Key Binding 规范正文和实现一致。
22
- - [ ] OIDC 标准引用和限制仍然有效。
23
- - [ ] 没有重复实现 DSH 已支持且适用的能力。
24
- - [ ] 兼容性矩阵和变更日志已更新。
25
-
26
- ## 安全与隐私
27
-
28
- - [ ] 已按实际部署拓扑审查威胁模型。
29
- - [ ] 单机 Credential Provider、Profile 归属与共享 Key 槽隔离边界已验证。
30
- - [ ] 已在预发布环境通过 OIDC 成功和反向符合性测试。
31
- - [ ] 已通过跨用户/跨 tenant Key Binding 授权测试。
32
- - [ ] 已测试 API Key 过期、轮换、停用和远程撤销行为。
33
- - [ ] 已确认 WebServer 仅监听 `127.0.0.1`,没有反向代理、端口转发或隧道把本地会话暴露给其他用户。
34
- - [ ] 已审查远端 TLS、CSP、Cookie、CSRF、出口、时钟、日志、APM 和备份。
35
- - [ ] 已完成身份和审计数据的隐私/法律审查。
36
- - [ ] 当前树与待公开 Git 历史的密钥扫描均未发现生产 host、ID、Key、日志、个人路径或个人数据。
37
-
38
- ## 供应链
39
-
40
- - [ ] Windows 和 Linux 上分别使用 Node 22、24 完成干净 `npm ci` 与 `npm run check`。
41
- - [ ] `npm audit` 和静态分析结果经过审查,而不只是执行。
42
- - [ ] 所有直接/间接许可证和例外已经审查。
43
- - [ ] npm 安装脚本已经审查,未预期脚本被拒绝。
44
- - [ ] Lockfile diff 已审查,依赖版本按预期锁定。
45
- - [ ] 公开发布前,GitHub Actions 已固定到不可变 commit。
46
-
47
- ## 产品验收
48
-
49
- - [ ] 纯 Web DSH:登录、callback、显示 name/sub 降级、provision、模型调用、刷新、检查连接和退出。
50
- - [ ] 桌面 backend:Wails/Electron 共用流程、取消和默认模型正确;仍需维护的旧 native 桥单独验证。
51
- - [ ] 原生多模态和纯文本 + image-transform 路由均已测试。
52
- - [ ] 不支持 reasoning effort 的模型仍能启用 thinking 并正常运行。
53
- - [ ] 已测试失败 UX:Discovery、token、UserInfo、bootstrap、策略拒绝、模型 Key、过期和网络超时。
54
-
55
- ## 构建与发布
56
-
57
- - [ ] 在干净 checkout 中通过 `npm run check`。
58
- - [ ] 已审查 `npm pack --dry-run` 和解压后的 tarball,且只包含预期文件。
59
- - [ ] Source map 不包含私有绝对路径或秘密。
60
- - [ ] Tag、发布说明和 checksum 已准备。
61
- - [ ] 实际发布路径已记录:Trusted Publisher 尚未配置时,手动 workflow 只以 `publish=false` 运行;首次稳定版可以使用维护者 npm CLI + 浏览器 2FA 发布冻结 tarball,且明确不声称 provenance。
62
- - [ ] 若使用 Trusted Publishing,Publisher 已绑定仓库 `freedomkk-qfeng/dsh-oidc`、workflow `release.yml` 和 environment `npm`;发布 job 使用固定 npm CLI `11.6.2`,且该提交的 Windows/Linux × Node 22/24 CI 已成功。
63
- - [ ] 发布输入与产物已按所选路径核验:CLI 路径核对冻结 tarball 身份、SHA-256、2FA 和 registry 结果;Trusted 路径还核对现有 `v<package version>` tag、提交、workflow artifact 和实际 npm provenance attestation。
64
- - [ ] 发布凭据没有写入仓库、日志或交付物;未配置 Trusted Publisher 时也不宣称使用了 Trusted Publishing。
65
- - [ ] 已确认回滚、弃用和漏洞通知计划。
7
+ - 核对变更行为、版本、变更记录、兼容范围和依赖锁。
8
+ - 在当前包目录执行 npm ci、npm run check;有行为变化时补对应 Host 或浏览器实测,使用合成测试数据。
9
+ - 检查实际 tarball、公开文档、许可证和导出入口,不携带凭据或用户数据。
10
+ - 在 packages-release.yml 中选择本包;publish=false 仅产出候选。明确发布时从匹配包版本的 tag 运行,使用 npm environment 与本仓库的 Trusted Publisher。
11
+ - 核验 registry 中的实际字节后再更新产品锁。发布 npm 不会发布桌面 Release 或替换客户端。
@@ -1,13 +1,15 @@
1
1
  # EduWork identity and resource integration (RFC EW-IDENTITY-1)
2
2
 
3
- > Scope update in .20260910.6: quota and activity heartbeat are institution-only extensions. The public package never requests /quota and its resources RPC contains only model metadata. No capability string or configuration flag enables quota in the public package. ECNU retains its existing server wire in a separate package.
3
+ > Capability scope: quota and activity heartbeat are institution-only extensions. The public package never requests /quota and its resources RPC contains only model metadata. No capability string or configuration flag enables quota in the public package. ECNU retains its existing server wire in a separate package.
4
4
 
5
- > Current candidate supports two modes: identity-only OIDC can omit all resource interfaces and use personal API keys. The complete organization-resource mode below adds managed credentials and models; see [Public resource protocol](public-resource-protocol.en.md). The mandatory requirements below apply to complete integration, not identity-only login.
5
+ > Two integration modes are supported: identity-only OIDC can omit all resource interfaces and use personal API keys. The complete organization-resource mode below adds managed credentials and models; see [Public resource protocol](public-resource-protocol.en.md). The mandatory requirements below apply to complete integration, not identity-only login.
6
6
 
7
7
  [简体中文](server-integration-contract.md) | **English**
8
8
 
9
9
  This document is written for identity-platform, model-platform, and integration-test teams. It defines, in API-reference form, the server capabilities required by `dsh-oidc`.
10
10
 
11
+ For the goals behind these interfaces and ways to contribute to their evolution, see the [Open Identity and Model Integration Initiative](open-integration.en.md). This specification remains the implementation and integration reference; the initiative does not change existing fields, paths, or compatibility rules.
12
+
11
13
  Status: implemented client wire specification, not an IETF standard. Source code and HTTP contract tests determine current behavior. Section 10 provides curl examples; section 11 contains **unimplemented optional proposals**. Every client is a local, single-user Runtime; this is not a shared Web-login architecture.
12
14
 
13
15
  | Capability | Required interfaces | Optionality and implemented behavior |
@@ -1,13 +1,15 @@
1
1
  # EduWork 身份与资源接入规范(RFC EW-IDENTITY-1)
2
2
 
3
- > .20260910.6 范围调整:配额与活跃心跳属于机构专属扩展。公版不请求 /quota,resources RPC 只返回模型元数据;任何 capabilities 字符串或公版配置开关都不会启用配额。ECNU 在独立机构包中沿用原配额服务协议。
3
+ > 能力范围:配额与活跃心跳属于机构专属扩展。公版不请求 /quota,resources RPC 只返回模型元数据;任何 capabilities 字符串或公版配置开关都不会启用配额。ECNU 在独立机构包中沿用原配额服务协议。
4
4
 
5
- > 当前候选支持两种模式:纯身份 OIDC 可省略全部资源接口,用户自配个人 Key;下文完整机构资源模式提供托管凭据与模型,见 [公共资源协议](public-resource-protocol.md)。下文的完整交付要求不适用于只做身份接入的场景。
5
+ > 支持两种接入模式:纯身份 OIDC 可省略全部资源接口,用户自配个人 Key;下文完整机构资源模式提供托管凭据与模型,见 [公共资源协议](public-resource-protocol.md)。下文的完整交付要求不适用于只做身份接入的场景。
6
6
 
7
7
  **简体中文** | [English](server-integration-contract.en.md)
8
8
 
9
9
  本文面向身份平台、模型平台和联调测试人员,以接口说明的形式定义接入 `dsh-oidc` 时服务端必须提供的能力。
10
10
 
11
+ 希望了解这套接口的目标或参与后续演进,请先看[开放身份与模型接入倡议](open-integration.md)。本规范是实现与联调依据,倡议不改变现有字段、路径或兼容性约定。
12
+
11
13
  状态:现有客户端 wire 规范;不是 IETF 标准。本文以 `@eduwork/dsh-oidc` 源码及 HTTP 契约测试为准。第 10 节列出需填入本次授权结果的 curl 请求示例;第 11 节是**尚未实现的可选扩展提案**。所有客户端都是用户本机的单用户 Runtime,不设计共享 Web 服务端登录会话。
12
14
 
13
15
  | 能力档 | 最小交付 | 可选性与当前行为 |
@@ -816,7 +818,7 @@ curl --fail-with-body --max-time 10 "$REVOCATION_ENDPOINT" \
816
818
 
817
819
  ### 11.1 机构资源发现
818
820
 
819
- 当前公版只读取本机可信 JSONC/Profile;**不会请求** `/.well-known/eduwork-configuration`。后续可约定此 HTTPS 入口返回纯数据 `protocol_version: eduwork-discovery/v1alpha1`,包含机构展示、OIDC Public Client、资源基址和能力名称。必须由用户/发行方明确导入,核对 issuer/provider/origin 后转换成本机配置;不得自动更换凭据目标,不得下发插件、脚本、CSS、可执行命令或任意模型 adapter。具体字段、签名/信任根、版本协商另行评审,不能把该提案当作本轮服务器必做接口。
821
+ 当前公版只读取本机可信 JSONC/Profile;**不会请求** `/.well-known/eduwork-configuration`。后续可约定此 HTTPS 入口返回纯数据 `protocol_version: eduwork-discovery/v1alpha1`,包含机构展示、OIDC Public Client、资源基址和能力名称。必须由用户/发行方明确导入,核对 issuer/provider/origin 后转换成本机配置;不得自动更换凭据目标,不得下发插件、脚本、CSS、可执行命令或任意模型 adapter。具体字段、签名/信任根、版本协商另行评审,不能把该提案当作当前协议的必需接口。
820
822
 
821
823
  ### 11.2 机构活跃心跳
822
824
 
@@ -1,78 +1,78 @@
1
- {
2
- "schemaVersion": "dsh-oidc/v1alpha1",
3
- "id": "example-university",
4
- "displayName": "Example University AI",
5
- "organization": "Example University",
6
- "brand": {
7
- "productName": "Example AI Work",
8
- "organizationName": "Example University",
9
- "mark": "E",
10
- "primaryColor": "#5157af",
11
- "loginTitle": "Sign in with Example University",
12
- "loginDescription": "Authenticate with the university identity provider and bind a model runtime credential. Your password never enters DSH.",
13
- "supportURL": "https://support.example.edu/ai"
14
- },
15
- "oidc": {
16
- "issuer": "https://id.example.edu/oidc",
17
- "clientId": "dsh-web-public-client",
18
- "scopes": [
19
- "openid",
20
- "profile",
21
- "offline_access",
22
- "worker.bootstrap.read",
23
- "worker.credential.provision",
24
- "worker.credential.read",
25
- "worker.credential.renew"
26
- ]
27
- },
28
- "keyBinding": {
29
- "baseURL": "https://ai.example.edu/api/worker/v1",
30
- "credentialRef": "EDUWORK_API_KEY"
31
- },
32
- "provider": {
33
- "id": "example-ai",
34
- "displayName": "Example AI",
35
- "adapter": "openai-compatible",
36
- "baseURL": "https://ai.example.edu/open/api/v1",
37
- "reasoning": "high",
38
- "defaultContextWindow": 262144,
39
- "defaultMaxTokens": 32768,
40
- "streamIdleTimeoutMs": 300000,
41
- "retryPolicy": {
42
- "mode": "normal",
43
- "maxRetries": 2
44
- },
45
- "compat": {
46
- "supportsDeveloperRole": false,
47
- "supportsStore": false,
48
- "supportsReasoningEffort": true,
49
- "supportsUsageInStreaming": true,
50
- "maxTokensField": "max_tokens",
51
- "supportsStrictMode": false,
52
- "supportsLongCacheRetention": false,
53
- "thinkingFormat": "deepseek",
54
- "requiresReasoningContentOnAssistantMessages": true
55
- },
56
- "models": [
57
- {
58
- "id": "example-max",
59
- "name": "Example Max",
60
- "input": ["text"],
61
- "reasoningEfforts": {
62
- "max": "max",
63
- "high": "high",
64
- "low": "low"
65
- },
66
- "defaultReasoningEffort": "high"
67
- },
68
- {
69
- "id": "example-plus",
70
- "name": "Example Plus",
71
- "input": ["text", "image"],
72
- "compat": {
73
- "supportsReasoningEffort": false
74
- }
75
- }
76
- ]
77
- }
78
- }
1
+ {
2
+ "schemaVersion": "dsh-oidc/v1alpha1",
3
+ "id": "example-university",
4
+ "displayName": "Example University AI",
5
+ "organization": "Example University",
6
+ "brand": {
7
+ "productName": "Example AI Work",
8
+ "organizationName": "Example University",
9
+ "mark": "E",
10
+ "primaryColor": "#5157af",
11
+ "loginTitle": "Sign in with Example University",
12
+ "loginDescription": "Authenticate with the university identity provider and bind a model runtime credential. Your password never enters DSH.",
13
+ "supportURL": "https://support.example.edu/ai"
14
+ },
15
+ "oidc": {
16
+ "issuer": "https://id.example.edu/oidc",
17
+ "clientId": "replace-with-public-client-id",
18
+ "scopes": [
19
+ "openid",
20
+ "profile",
21
+ "offline_access",
22
+ "worker.bootstrap.read",
23
+ "worker.credential.provision",
24
+ "worker.credential.read",
25
+ "worker.credential.renew"
26
+ ]
27
+ },
28
+ "keyBinding": {
29
+ "baseURL": "https://ai.example.edu/api/worker/v1",
30
+ "credentialRef": "EDUWORK_API_KEY"
31
+ },
32
+ "provider": {
33
+ "id": "example-ai",
34
+ "displayName": "Example AI",
35
+ "adapter": "openai-compatible",
36
+ "baseURL": "https://ai.example.edu/open/api/v1",
37
+ "reasoning": "high",
38
+ "defaultContextWindow": 262144,
39
+ "defaultMaxTokens": 32768,
40
+ "streamIdleTimeoutMs": 300000,
41
+ "retryPolicy": {
42
+ "mode": "normal",
43
+ "maxRetries": 2
44
+ },
45
+ "compat": {
46
+ "supportsDeveloperRole": false,
47
+ "supportsStore": false,
48
+ "supportsReasoningEffort": true,
49
+ "supportsUsageInStreaming": true,
50
+ "maxTokensField": "max_tokens",
51
+ "supportsStrictMode": false,
52
+ "supportsLongCacheRetention": false,
53
+ "thinkingFormat": "deepseek",
54
+ "requiresReasoningContentOnAssistantMessages": true
55
+ },
56
+ "models": [
57
+ {
58
+ "id": "example-max",
59
+ "name": "Example Max",
60
+ "input": ["text"],
61
+ "reasoningEfforts": {
62
+ "max": "max",
63
+ "high": "high",
64
+ "low": "low"
65
+ },
66
+ "defaultReasoningEffort": "high"
67
+ },
68
+ {
69
+ "id": "example-plus",
70
+ "name": "Example Plus",
71
+ "input": ["text", "image"],
72
+ "compat": {
73
+ "supportsReasoningEffort": false
74
+ }
75
+ }
76
+ ]
77
+ }
78
+ }
@@ -4,7 +4,7 @@
4
4
  "displayName": "Example Organization",
5
5
  "oidc": {
6
6
  "issuer": "https://identity.example.edu",
7
- "clientId": "example-public-client",
7
+ "clientId": "replace-with-public-client-id",
8
8
  "scopes": ["openid", "profile", "offline_access"]
9
9
  }
10
10
  }
@@ -4,7 +4,7 @@
4
4
  "displayName": "Example Organization",
5
5
  "oidc": {
6
6
  "issuer": "https://identity.example.edu",
7
- "clientId": "example-public-client",
7
+ "clientId": "replace-with-public-client-id",
8
8
  "scopes": ["openid", "profile", "offline_access"]
9
9
  },
10
10
  "keyBinding": {
@@ -56,4 +56,3 @@ export class EnterpriseModelTransforms extends Service {
56
56
  return transformed
57
57
  }
58
58
  }
59
-
@@ -1,44 +1,44 @@
1
- import {
1
+ import {
2
2
  accountResult, baseURLSchema, beginResult, configurationResult, configurationTargetSchema, openConfigurationResult, jsonParameter, managementResult, loginIdSchema, loginResult,
3
3
  modelCatalogModeSchema, profileIdSchema, reconcileOptionsSchema, restartResult, runtimeModelsSchema, resourcesResult, enterpriseSelectionResult, enterpriseSelectionOptionsSchema,
4
- } from './typert-schemas.js'
5
-
6
- const pkg = '@eduwork/dsh-oidc'
7
- const source = { file: 'lib/index.js', line: 1, column: 1 }
8
- const profile = () => jsonParameter('profileID', profileIdSchema, `${pkg}#ProfileID`)
9
- const login = () => jsonParameter('loginID', loginIdSchema, `${pkg}#LoginID`)
10
- const options = () => jsonParameter('options', reconcileOptionsSchema, `${pkg}#ReconcileOptions`)
11
- const baseURL = () => jsonParameter('baseURL', baseURLSchema, `${pkg}#BaseURL`)
12
- const modelMode = () => jsonParameter('modelMode', modelCatalogModeSchema, `${pkg}#ModelCatalogMode`)
13
- const models = () => jsonParameter('models', runtimeModelsSchema, `${pkg}#RuntimeModels`)
14
- const descriptor = (method, parameters, result) => ({
15
- id: `${pkg}#oidcAccounts/${method}`, service: 'oidcAccounts', namespace: 'oidcAccounts', method,
16
- invocation: { kind: 'direct' }, parameters, result, sourceLocation: source,
17
- })
18
-
19
- export const TYPERT = {
20
- package: pkg, face: 'host', schemas: [],
21
- invocations: [
4
+ } from './typert-schemas.js'
5
+
6
+ const pkg = '@eduwork/dsh-oidc'
7
+ const source = { file: 'lib/index.js', line: 1, column: 1 }
8
+ const profile = () => jsonParameter('profileID', profileIdSchema, `${pkg}#ProfileID`)
9
+ const login = () => jsonParameter('loginID', loginIdSchema, `${pkg}#LoginID`)
10
+ const options = () => jsonParameter('options', reconcileOptionsSchema, `${pkg}#ReconcileOptions`)
11
+ const baseURL = () => jsonParameter('baseURL', baseURLSchema, `${pkg}#BaseURL`)
12
+ const modelMode = () => jsonParameter('modelMode', modelCatalogModeSchema, `${pkg}#ModelCatalogMode`)
13
+ const models = () => jsonParameter('models', runtimeModelsSchema, `${pkg}#RuntimeModels`)
14
+ const descriptor = (method, parameters, result) => ({
15
+ id: `${pkg}#oidcAccounts/${method}`, service: 'oidcAccounts', namespace: 'oidcAccounts', method,
16
+ invocation: { kind: 'direct' }, parameters, result, sourceLocation: source,
17
+ })
18
+
19
+ export const TYPERT = {
20
+ package: pkg, face: 'host', schemas: [],
21
+ invocations: [
22
22
  descriptor('configuration', [], configurationResult),
23
23
  descriptor('openConfiguration', [jsonParameter('target', configurationTargetSchema, `${pkg}#ConfigurationTarget`)], openConfigurationResult),
24
- descriptor('status', [profile()], accountResult),
25
- descriptor('resources', [profile()], resourcesResult),
26
- descriptor('begin', [profile()], beginResult),
27
- descriptor('loginStatus', [login()], loginResult),
28
- descriptor('cancelLogin', [login()], loginResult),
24
+ descriptor('status', [profile()], accountResult),
25
+ descriptor('resources', [profile()], resourcesResult),
26
+ descriptor('begin', [profile()], beginResult),
27
+ descriptor('loginStatus', [login()], loginResult),
28
+ descriptor('cancelLogin', [login()], loginResult),
29
29
  descriptor('reconcile', [profile(), options()], accountResult),
30
30
  descriptor('selectEnterpriseModel', [profile(), jsonParameter('options', enterpriseSelectionOptionsSchema, `${pkg}#EnterpriseSelectionOptions`)], enterpriseSelectionResult),
31
- descriptor('logout', [profile()], accountResult),
32
- descriptor('management', [], managementResult),
33
- descriptor('activate', [profile()], managementResult),
34
- descriptor('configure', [profile()], managementResult),
35
- descriptor('addCustom', [baseURL()], managementResult),
36
- descriptor('updateCustom', [profile(), baseURL()], managementResult),
37
- descriptor('removeProfile', [profile()], managementResult),
38
- descriptor('configureModels', [profile(), modelMode(), models()], managementResult),
39
- descriptor('restart', [], restartResult),
40
- ],
41
- model: { services: [], events: [], objects: [] },
42
- }
43
-
44
- export default TYPERT
31
+ descriptor('logout', [profile()], accountResult),
32
+ descriptor('management', [], managementResult),
33
+ descriptor('activate', [profile()], managementResult),
34
+ descriptor('configure', [profile()], managementResult),
35
+ descriptor('addCustom', [baseURL()], managementResult),
36
+ descriptor('updateCustom', [profile(), baseURL()], managementResult),
37
+ descriptor('removeProfile', [profile()], managementResult),
38
+ descriptor('configureModels', [profile(), modelMode(), models()], managementResult),
39
+ descriptor('restart', [], restartResult),
40
+ ],
41
+ model: { services: [], events: [], objects: [] },
42
+ }
43
+
44
+ export default TYPERT
@@ -1,43 +1,43 @@
1
- import {
1
+ import {
2
2
  accountResult, baseURLSchema, beginResult, configurationResult, configurationTargetSchema, openConfigurationResult, jsonParameter, managementResult, loginIdSchema, loginResult,
3
3
  modelCatalogModeSchema, profileIdSchema, reconcileOptionsSchema, restartResult, runtimeModelsSchema, resourcesResult, enterpriseSelectionResult, enterpriseSelectionOptionsSchema,
4
- } from './typert-schemas.js'
5
-
6
- const pkg = '@eduwork/dsh-oidc'
7
- const source = { file: 'lib/index.js', line: 1, column: 1 }
8
- const profile = () => jsonParameter('profileID', profileIdSchema, `${pkg}#ProfileID`)
9
- const login = () => jsonParameter('loginID', loginIdSchema, `${pkg}#LoginID`)
10
- const options = () => jsonParameter('options', reconcileOptionsSchema, `${pkg}#ReconcileOptions`)
11
- const baseURL = () => jsonParameter('baseURL', baseURLSchema, `${pkg}#BaseURL`)
12
- const modelMode = () => jsonParameter('modelMode', modelCatalogModeSchema, `${pkg}#ModelCatalogMode`)
13
- const models = () => jsonParameter('models', runtimeModelsSchema, `${pkg}#RuntimeModels`)
14
- const descriptor = (method, parameters, result) => ({
15
- id: `${pkg}#oidcAccounts/${method}`, service: 'oidcAccounts', namespace: 'oidcAccounts', method,
16
- invocation: { kind: 'direct' }, parameters, result, sourceLocation: source,
17
- })
18
-
19
- export const TYPERT_REMOTE = {
20
- package: pkg,
21
- descriptors: [
4
+ } from './typert-schemas.js'
5
+
6
+ const pkg = '@eduwork/dsh-oidc'
7
+ const source = { file: 'lib/index.js', line: 1, column: 1 }
8
+ const profile = () => jsonParameter('profileID', profileIdSchema, `${pkg}#ProfileID`)
9
+ const login = () => jsonParameter('loginID', loginIdSchema, `${pkg}#LoginID`)
10
+ const options = () => jsonParameter('options', reconcileOptionsSchema, `${pkg}#ReconcileOptions`)
11
+ const baseURL = () => jsonParameter('baseURL', baseURLSchema, `${pkg}#BaseURL`)
12
+ const modelMode = () => jsonParameter('modelMode', modelCatalogModeSchema, `${pkg}#ModelCatalogMode`)
13
+ const models = () => jsonParameter('models', runtimeModelsSchema, `${pkg}#RuntimeModels`)
14
+ const descriptor = (method, parameters, result) => ({
15
+ id: `${pkg}#oidcAccounts/${method}`, service: 'oidcAccounts', namespace: 'oidcAccounts', method,
16
+ invocation: { kind: 'direct' }, parameters, result, sourceLocation: source,
17
+ })
18
+
19
+ export const TYPERT_REMOTE = {
20
+ package: pkg,
21
+ descriptors: [
22
22
  descriptor('configuration', [], configurationResult),
23
23
  descriptor('openConfiguration', [jsonParameter('target', configurationTargetSchema, `${pkg}#ConfigurationTarget`)], openConfigurationResult),
24
- descriptor('status', [profile()], accountResult),
25
- descriptor('resources', [profile()], resourcesResult),
26
- descriptor('begin', [profile()], beginResult),
27
- descriptor('loginStatus', [login()], loginResult),
28
- descriptor('cancelLogin', [login()], loginResult),
24
+ descriptor('status', [profile()], accountResult),
25
+ descriptor('resources', [profile()], resourcesResult),
26
+ descriptor('begin', [profile()], beginResult),
27
+ descriptor('loginStatus', [login()], loginResult),
28
+ descriptor('cancelLogin', [login()], loginResult),
29
29
  descriptor('reconcile', [profile(), options()], accountResult),
30
30
  descriptor('selectEnterpriseModel', [profile(), jsonParameter('options', enterpriseSelectionOptionsSchema, `${pkg}#EnterpriseSelectionOptions`)], enterpriseSelectionResult),
31
- descriptor('logout', [profile()], accountResult),
32
- descriptor('management', [], managementResult),
33
- descriptor('activate', [profile()], managementResult),
34
- descriptor('configure', [profile()], managementResult),
35
- descriptor('addCustom', [baseURL()], managementResult),
36
- descriptor('updateCustom', [profile(), baseURL()], managementResult),
37
- descriptor('removeProfile', [profile()], managementResult),
38
- descriptor('configureModels', [profile(), modelMode(), models()], managementResult),
39
- descriptor('restart', [], restartResult),
40
- ],
41
- }
42
-
43
- export default TYPERT_REMOTE
31
+ descriptor('logout', [profile()], accountResult),
32
+ descriptor('management', [], managementResult),
33
+ descriptor('activate', [profile()], managementResult),
34
+ descriptor('configure', [profile()], managementResult),
35
+ descriptor('addCustom', [baseURL()], managementResult),
36
+ descriptor('updateCustom', [profile(), baseURL()], managementResult),
37
+ descriptor('removeProfile', [profile()], managementResult),
38
+ descriptor('configureModels', [profile(), modelMode(), models()], managementResult),
39
+ descriptor('restart', [], restartResult),
40
+ ],
41
+ }
42
+
43
+ export default TYPERT_REMOTE