# dsh-oauth

[English](README.md) | 简体中文

`dsh-oauth` 是 DeepSeek Harness 的提供方中立 OAuth 基座。它不内置 OpenAI、ChatGPT、Codex 或任何其他厂商协议，而是给独立提供方插件一套稳定的账号与凭据生命周期。

```text
提供方插件                dsh-oauth                    DeepSeek Harness
端点、client、scope  ->  登录会话与挑战  ->  ctx.credentials / 设置页 / Typert Remote
刷新、撤销、账号解析       并发刷新与账号索引
模型适配器                 不参与模型请求
```

## 基座负责什么

- 注册和卸载 OAuth 提供方驱动。
- 管理浏览器链接、设备码、一次性输入、取消和超时。
- 将 OAuth 凭据存入 DSH 的 `ctx.credentials`，Remote 和 Web 界面从不返回令牌。
- 原子维护不含机密的账号索引，并按账号串行执行跨进程 token refresh。
- 向 Host 侧适配器提供 `getCredential()` 和 `getAccessToken()`。
- 在宿主模型页的提供方添加区域保留“登录账号”兼容入口，并在 `dsh-model-manager` 的独立“模型中心”中提供同一套 OAuth 登录和账号配置。两处入口都复用同一个提供方中立的登录流程。

## 基座不负责什么

- 不声明 OAuth endpoint、client ID、scope 或厂商附加参数。
- 不实现 OpenAI、Codex、Claude、Google 等具体登录协议。
- 不注册模型目录，不实现 LLM adapter，不代理模型请求。
- 不创建或修改模型提供方配置；登录成功后由具体提供方插件向 OAuth 子槽注册配置条目，并自行维护模型路由。独立模型中心是后续演进入口，宿主原模型页目前继续保留。
- 不把第三方订阅自动解释为通用 API 权限；可用模型和服务条款由提供方插件与第三方服务决定。

## 提供方插件接入

```ts
import type { Context } from '@deepseek-ai/cordis'
import type {} from 'dsh-oauth'
import type { OAuthProviderDriver } from 'dsh-oauth/types'

const driver: OAuthProviderDriver = {
  id: 'example',
  displayName: 'Example Account',
  authorizationTimeoutMs: 900000,
  async authorize(interaction, signal) {
    interaction.publish({ kind: 'browser', url: 'https://example.test/oauth/authorize' })
    // 提供方插件完成 PKCE、回调、token exchange 和账号解析。
    return {
      account: { id: 'stable-account-id', displayName: 'Example User' },
      credential: { schemaVersion: 1, accessToken: '...', refreshToken: '...' },
    }
  },
  async refresh(credential, signal) {
    return { credential }
  },
}

export function apply(ctx: Context): void {
  ctx.effect(() => ctx.oauth.registerProvider(driver), 'example-oauth-provider')
}

export const inject = ['oauth']
```

提供方可以用 `authorizationTimeoutMs` 声明自身授权流程所需的超时；未声明时使用基座的 `loginTimeoutMs`。这让设备码等长流程可以独立设置时限，而不在基座中加入厂商分支。

完整约定见 [提供方驱动指南](docs/provider-driver.zh-CN.md) 和 [架构设计](docs/design.zh-CN.md)。

## 安装

当前仓库只交付基座，没有真实提供方。安装基座后还需要安装至少一个依赖 `dsh-oauth` 的提供方插件：

```sh
dsh plugin --profile web add ./dsh-oauth-0.1.1.tgz
dsh web
```

## 配置

```yaml
- id: dsh-oauth
  name: dsh-oauth
  config:
    loginTimeoutMs: 600000
    sessionRetentionMs: 1800000
    refreshBeforeMs: 300000
```

可选的 `dshHome` 和 `metadataPath` 用于覆盖账号索引位置。默认索引位于 `$DSH_HOME/oauth/accounts.json`；文件只包含提供方 ID、账号显示信息、凭据引用和时间戳，不包含 access token 或 refresh token。

## 浏览器状态持久化

- 普通字段：提供方搜索词写入 `dsh.oauth.settings.global.search.v1`。
- 临时字段：设备码、验证码、密码和登录会话不写入 `localStorage`。
- 机密字段：Web 界面不持有 OAuth 凭据，因此没有浏览器端机密持久化。

## 开发

```sh
pnpm install
pnpm run typecheck
pnpm test
pnpm run build
pnpm run pack:check
```

## 社区调研

调研和自研决策见 [OAuth 方案调研](docs/research.zh-CN.md)。已有项目可以作为具体提供方实现的参考，但它们都把 OpenAI/Codex 协议和模型适配绑在一起，不适合作为提供方中立基座的运行时依赖。

## License

MIT
