dsh-openai-subscription 0.1.0 → 0.1.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
@@ -2,13 +2,16 @@
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- Use a ChatGPT subscription in [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) through OpenAI device authorization. No API key is required. After sign-in, the plugin attempts to add supported GPT models to the DSH model picker without manual provider setup.
5
+ Use a ChatGPT subscription with Codex access in [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) through OpenAI device authorization. No API key or manual declaration of newly available account models is required.
6
6
 
7
7
  ## Features
8
8
 
9
- - Sign in with a ChatGPT plan that includes Codex access
10
- - Attempt to enable the `openai-codex` provider and supported GPT models automatically
11
- - View account status, refresh authorization, or sign out from DSH settings
9
+ - Connect DSH to a ChatGPT plan that includes Codex access
10
+ - Dynamically read account-visible models from the ChatGPT Codex backend
11
+ - Safely merge live models with the `openai-codex` catalog from DSH's installed adapter version
12
+ - Preserve user-added models, field edits, and deletion choices across later syncs
13
+ - Provide focused actions for connecting, model sync, authorization recovery, and local disconnect
14
+ - Follow the current DSH locale in Chinese and English
12
15
  - Keep access and refresh tokens in the local DSH credential store
13
16
 
14
17
  ## Requirements
@@ -33,37 +36,63 @@ Install the plugin into the Web profile:
33
36
  dsh plugin --profile web add dsh-openai-subscription
34
37
  ```
35
38
 
36
- Restart `dsh web`, then refresh the page. DSH registers the plugin automatically; no profile edits are required.
39
+ Restart `dsh web`, then refresh the existing page. DSH registers the plugin automatically; no profile edits are required.
37
40
 
38
41
  ## Usage
39
42
 
40
- 1. Open **Settings → ChatGPT 订阅登录** (ChatGPT Subscription Sign-in).
41
- 2. Select **使用 ChatGPT 账号登录** (Sign in with ChatGPT).
42
- 3. Open the displayed link and enter the device code with your ChatGPT account.
43
- 4. Return to DSH after authorization completes and select a supported GPT model.
43
+ 1. Open **Settings → ChatGPT Subscription**.
44
+ 2. Select **Connect ChatGPT**.
45
+ 3. Open the verification page and enter the displayed one-time device code with your ChatGPT account.
46
+ 4. Return to DSH after authorization completes; the plugin fetches and syncs models automatically.
47
+ 5. Select an available model from the DSH model picker.
44
48
 
45
- Use **刷新授权** (Refresh authorization) if the session cannot renew automatically. Use **退出登录** (Sign out) before changing accounts or uninstalling the plugin.
49
+ `llm-pi-ai` automatically renews refreshable OAuth credentials during model requests. **Refresh authorization** in this settings page is primarily a manual recovery action.
50
+
51
+ ### Model synchronization rules
52
+
53
+ DSH treats `openai-codex.models` as a complete replacement list, not an additive list. Adding one model manually therefore hides the previous implicit built-in catalog. This plugin writes a complete, owned snapshot instead:
54
+
55
+ - Models returned by the account endpoint with `visibility=list` are the dynamic source.
56
+ - Built-in model IDs from DSH's currently installed `llm-pi-ai` adapter supplement IDs the account response does not mention at all.
57
+ - An ID explicitly marked hidden by the account endpoint is never resurrected from the built-in catalog.
58
+ - Account metadata wins for the same ID. Built-in-only rows are written as `{ id }` so `llm-pi-ai` continues supplying their complete capabilities.
59
+ - Later syncs use a field-level three-way merge. User-edited fields, local additions, and models removed from a non-empty explicit list remain preserved.
60
+ - Fetch, parse, or settings-write failures never replace current settings with a partial result.
61
+
62
+ If an explicit `models` list already exists in either the user layer or a profile base layer before the first sync, the plugin cannot safely infer whether it is an intentional allow-list or a list of additions. Automatic sign-in sync leaves it untouched. The settings page then shows that syncing is needed; selecting **Sync models** and confirming explicitly opts into merging the existing entries as local configuration.
63
+
64
+ ### Disconnecting
65
+
66
+ **Disconnect** removes only locally stored authorization and unchanged model rows managed by this plugin. It preserves locally customized models and provider fields, and it neither signs out of nor deletes the ChatGPT account. A confirmation is shown first.
67
+
68
+ Disconnect before changing accounts or uninstalling the plugin.
46
69
 
47
70
  ## Uninstallation
48
71
 
49
- Sign out from the settings page first so the stored authorization is removed, then run:
72
+ Select **Disconnect** in the settings page first, then run:
50
73
 
51
74
  ```sh
52
75
  dsh plugin --profile web remove dsh-openai-subscription
53
76
  ```
54
77
 
55
- Restart the Web profile afterward.
78
+ Restart the Web profile and refresh the page afterward.
56
79
 
57
80
  ## Troubleshooting
58
81
 
59
82
  - **The settings entry is missing:** confirm the plugin is installed in the `web` profile with `dsh plugin --profile web why dsh-openai-subscription`, then restart DSH and refresh the page.
60
83
  - **Authorization cannot start:** update DSH, confirm `node --version` meets the requirement, and check access to the OpenAI authentication service.
61
- - **The device code is rejected:** enable device-code authorization in the ChatGPT security settings and start a new sign-in.
62
- - **Refresh fails:** sign out and complete device authorization again.
84
+ - **The device code is rejected:** enable device-code authorization in ChatGPT security settings and start a new connection.
85
+ - **Models need syncing:** select **Sync models**. If an explicit list already exists, review the confirmation and continue; local edits are preserved.
86
+ - **Model sync fails:** existing settings remain unchanged. Check access to `chatgpt.com` and authorization status, then retry; refresh authorization first if needed.
87
+ - **Authorization cannot recover:** disconnect and complete device authorization again.
88
+
89
+ ## Compatibility notes
90
+
91
+ Dynamic discovery uses the backend endpoint used by the official ChatGPT Codex client, but that endpoint is not a public, stability-guaranteed OpenAI API and may change. It does not provide complete output-limit, cost, or wire-compatibility metadata. `llm-pi-ai` fills metadata for known models; unknown models use adapter defaults. Catalog presence also does not guarantee that an older adapter supports every new model capability, so update DSH first when calls reveal compatibility problems.
63
92
 
64
93
  ## Security and privacy
65
94
 
66
- Your password is entered only on OpenAI's website; the plugin never receives it. OAuth credentials are stored in the local DSH credential store, and the status interface never returns token values. Do not share the temporary device code.
95
+ Your password is entered only on OpenAI's website; the plugin never receives it. OAuth credentials remain in the local DSH credential store. The settings status API returns only semantic facts such as connection, refresh capability, model-sync state, and model count. It does not return tokens, internal account IDs, or exact token-expiry timestamps. Do not share the temporary device code.
67
96
 
68
97
  OpenAI's terms, privacy policy, model availability, and usage limits still apply. This is an independent community plugin and is not affiliated with or endorsed by OpenAI or DeepSeek.
69
98
 
package/README.zh.md CHANGED
@@ -2,13 +2,16 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 通过 OpenAI 设备授权,在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 中使用 ChatGPT 订阅。无需 API 密钥。登录后,插件会尝试自动将受支持的 GPT 模型加入 DSH 模型选择器。
5
+ 通过 OpenAI 设备授权,在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 中使用包含 Codex 权限的 ChatGPT 订阅。无需 API 密钥,也无需手动声明新出现的账号模型。
6
6
 
7
7
  ## 功能
8
8
 
9
- - 使用包含 Codex 权限的 ChatGPT 套餐登录
10
- - 尝试自动启用 `openai-codex` 提供商和受支持的 GPT 模型
11
- - DSH 设置中查看账号状态、刷新授权或退出登录
9
+ - 使用包含 Codex 权限的 ChatGPT 套餐连接 DSH
10
+ - ChatGPT Codex 后端动态读取当前账号可见的模型
11
+ - 将动态模型与 DSH 实际安装版本的 `openai-codex` 内置目录安全合并
12
+ - 在后续同步中保留用户新增模型、字段编辑和删除选择
13
+ - 提供连接、模型同步、授权恢复和本机断开操作
14
+ - 根据 DSH 当前语言显示中文或英文界面
12
15
  - 将访问令牌和刷新令牌保存在本机 DSH 凭证库中
13
16
 
14
17
  ## 使用要求
@@ -33,37 +36,63 @@ npm install -g pnpm @deepseek-ai/dsh@next
33
36
  dsh plugin --profile web add dsh-openai-subscription
34
37
  ```
35
38
 
36
- 重启 `dsh web`,然后刷新页面。DSH 会自动注册插件,无需手动修改 profile。
39
+ 重启 `dsh web`,然后刷新现有页面。DSH 会自动注册插件,无需手动修改 profile。
37
40
 
38
41
  ## 使用
39
42
 
40
- 1. 打开 **设置 → ChatGPT 订阅登录**。
41
- 2. 点击 **使用 ChatGPT 账号登录**。
42
- 3. 打开页面中显示的链接,使用 ChatGPT 账号输入设备码。
43
- 4. 授权完成后返回 DSH,选择受支持的 GPT 模型。
43
+ 1. 打开 **设置 → ChatGPT 订阅**。
44
+ 2. 点击 **连接 ChatGPT**。
45
+ 3. 打开验证页面,使用 ChatGPT 账号输入页面中的一次性设备码。
46
+ 4. 授权完成后返回 DSH;插件会自动获取并同步模型。
47
+ 5. 在 DSH 的模型选择器中选择可用模型。
44
48
 
45
- 会话无法自动续期时,可点击 **刷新授权**。更换账号或卸载插件前,请先点击 **退出登录**。
49
+ `llm-pi-ai` 会在模型请求期间自动续期可刷新的 OAuth 凭证。设置页中的 **刷新授权** 主要用于授权异常时的手动恢复。
50
+
51
+ ### 模型同步规则
52
+
53
+ DSH 的 `openai-codex.models` 是完整替代列表,而不是可追加列表;只手动添加一个模型会遮蔽原来的隐式内置目录。此插件因此写入一份完整、可追踪的快照:
54
+
55
+ - 账号端返回且 `visibility=list` 的模型作为动态来源。
56
+ - DSH 当前安装的 `llm-pi-ai` 内置模型会补充账号响应中完全未提及的 ID。
57
+ - 账号端明确标记为隐藏的 ID 不会被内置目录重新加入。
58
+ - 同一 ID 的账号端元数据优先于内置元数据;内置专属条目只写 `{ id }`,其余能力仍由 `llm-pi-ai` 补全。
59
+ - 后续同步执行字段级三方合并:用户改过的字段、本地新增条目以及从非空列表中删除的模型都会保留。
60
+ - 获取、解析或设置写入失败时,不会用不完整结果覆盖现有模型设置。
61
+
62
+ 如果登录前已经存在用户层或 profile 基础层的显式 `models` 列表,插件不会在首次自动同步时推断它是“额外模型”还是有意的 allow-list。此时设置页会显示需要同步;点击 **同步模型** 并确认,才会把现有条目作为本地配置合并进去。
63
+
64
+ ### 断开连接
65
+
66
+ **断开连接** 只删除本机保存的授权,以及仍未被用户修改的插件管理模型项。它会保留本地自定义模型和 provider 字段,也不会注销或删除 ChatGPT 账号。操作前会显示确认提示。
67
+
68
+ 更换账号或卸载插件前,建议先断开连接。
46
69
 
47
70
  ## 卸载
48
71
 
49
- 先在设置页退出登录以删除本机授权,再执行:
72
+ 先在设置页点击 **断开连接**,再执行:
50
73
 
51
74
  ```sh
52
75
  dsh plugin --profile web remove dsh-openai-subscription
53
76
  ```
54
77
 
55
- 随后重启 Web profile
78
+ 随后重启 Web profile 并刷新页面。
56
79
 
57
80
  ## 故障排查
58
81
 
59
- - **设置中没有登录入口:** 使用 `dsh plugin --profile web why dsh-openai-subscription` 确认插件已安装到 `web` profile,然后重启 DSH 并刷新页面。
82
+ - **设置中没有入口:** 使用 `dsh plugin --profile web why dsh-openai-subscription` 确认插件已安装到 `web` profile,然后重启 DSH 并刷新页面。
60
83
  - **无法开始授权:** 更新 DSH,确认 `node --version` 满足要求,并检查 OpenAI 登录服务是否可访问。
61
- - **设备码被拒绝:** 在 ChatGPT 安全设置中启用设备码授权,然后重新登录。
62
- - **刷新授权失败:** 退出登录后重新完成设备授权。
84
+ - **设备码被拒绝:** 在 ChatGPT 安全设置中启用设备码授权,然后重新连接。
85
+ - **模型需要同步:** 点击 **同步模型**。若已有显式模型列表,请阅读确认提示后继续;插件会保留本地编辑。
86
+ - **模型同步失败:** 现有设置会保持不变。检查 `chatgpt.com` 的网络访问和授权状态,稍后重试;必要时先刷新授权。
87
+ - **授权无法恢复:** 断开连接后重新完成设备授权。
88
+
89
+ ## 兼容性说明
90
+
91
+ 动态目录使用 ChatGPT 官方 Codex 客户端所使用的后端接口,但它不是公开、稳定承诺的 OpenAI API,未来可能变化。接口不提供完整的输出上限、费用或 wire compatibility 元数据;已知模型由 `llm-pi-ai` 补全,未知模型使用适配器默认值。目录中出现模型也不保证旧版适配器支持其所有新能力,遇到调用兼容问题时请先更新 DSH。
63
92
 
64
93
  ## 安全与隐私
65
94
 
66
- 密码只在 OpenAI 官方页面输入,本插件不会接触密码。OAuth 凭证保存在本机 DSH 凭证库中,状态接口不会返回令牌内容。设备验证码在有效期内属于敏感信息,请勿分享。
95
+ 密码只在 OpenAI 官方页面输入,本插件不会接触密码。OAuth 凭证保存在本机 DSH 凭证库中。设置页状态接口只返回“是否连接、是否可刷新、模型是否同步、模型数量”等语义信息,不返回令牌、内部账号 ID 或精确令牌到期时间。设备验证码在有效期内属于敏感信息,请勿分享。
67
96
 
68
97
  使用过程仍受 OpenAI 的条款、隐私政策、模型可用性和用量限制约束。本项目是独立的社区插件,与 OpenAI 或 DeepSeek 无隶属或背书关系。
69
98
 
package/dist/client.d.ts CHANGED
@@ -1,32 +1,31 @@
1
- /** Notice queued by the host during an authorization attempt. */
2
1
  interface FlowNotice {
3
- message?: string;
4
- url?: string | null;
5
- code?: string | null;
2
+ kind?: 'requesting-code' | 'enter-code' | 'refreshing' | 'models-synced' | 'models-sync-failed';
3
+ url?: string;
4
+ code?: string;
6
5
  }
7
- /** `openaiSubscription/status` reply. */
8
6
  interface StatusInfo {
9
- configured?: boolean;
10
- ready?: boolean;
11
- accountId?: string | null;
12
- expires?: number | null;
7
+ configured: boolean;
8
+ ready: boolean;
9
+ refreshable: boolean;
10
+ modelsSynced: boolean;
11
+ modelCount: number;
13
12
  }
14
- /** `openaiSubscription/poll` reply. */
15
13
  interface PollInfo {
16
- status?: 'idle' | 'pending' | 'done';
17
- notices?: FlowNotice[];
18
- outcome?: string | null;
19
- error?: string | null;
14
+ status: 'idle' | 'pending' | 'done';
15
+ notices: FlowNotice[];
16
+ outcome: 'authorized' | 'cancelled' | 'failed' | null;
20
17
  }
21
- /** `openaiSubscription/authorize` reply. */
22
18
  interface AuthorizeInfo {
23
- started?: boolean;
24
- error?: string;
19
+ started: boolean;
20
+ }
21
+ interface ModelSyncInfo {
22
+ synced: true;
23
+ count: number;
25
24
  }
26
- /** Envelope of `connection.rpc.call`. */
27
25
  interface RemoteResult<T> {
28
26
  ok?: boolean;
29
27
  error?: {
28
+ code?: string;
30
29
  message?: string;
31
30
  } | null;
32
31
  value?: T;
@@ -42,22 +41,30 @@ interface ConnectionService {
42
41
  interface ClientTimer {
43
42
  interval(callback: () => void, delay: number): () => void;
44
43
  }
45
- /** Context passed to the client plugin. */
44
+ type Translate = (key: string, params?: Record<string, string | number>) => string;
45
+ interface LocaleService {
46
+ register(namespace: string, dictionaries: Record<string, Record<string, string>>): () => void;
47
+ bind(namespace: string): Translate;
48
+ }
46
49
  interface ClientContext {
47
50
  get(name: string): unknown;
48
51
  connection: ConnectionService;
49
52
  timer: ClientTimer | undefined;
53
+ locale?: LocaleService;
54
+ effect?(setup: () => void | (() => void), label?: string): unknown;
55
+ on?(event: string, listener: () => void): () => void;
56
+ }
57
+ interface SettingsSectionMeta {
58
+ name: string;
59
+ id: string;
60
+ order: number;
61
+ label: string | (() => string);
62
+ locale?: string;
50
63
  }
51
64
  interface SlotsService {
52
65
  inject(name: string, setup: () => void): unknown;
53
- register(meta: {
54
- name: string;
55
- id: string;
56
- order: number;
57
- label: string;
58
- }, render: () => unknown): unknown;
59
- }
60
- /** Exports the web module table expects from a `dsh.client` bundle. */
66
+ register(meta: SettingsSectionMeta, render: () => unknown): unknown;
67
+ }
61
68
  interface ClientModuleExports {
62
69
  apply(ctx: ClientContext): void;
63
70
  inject: string[];
@@ -81,6 +88,8 @@ interface ReactElement {
81
88
  interface SectionProps {
82
89
  connection: ConnectionService;
83
90
  timer: ClientTimer | undefined;
91
+ t: Translate;
92
+ subscribeReset?: (listener: () => void) => () => void;
84
93
  }
85
94
  type SectionComponent = (props: SectionProps) => ReactElement;
86
95
  interface ReactModule {
@@ -91,5 +100,7 @@ interface ReactModule {
91
100
  current: T;
92
101
  };
93
102
  }
94
- /** Stringify an unknown thrown value for display. */
95
- declare function messageOf(error: unknown): string;
103
+ declare const NS = "settings.openaiSubscription";
104
+ declare const ZH: Record<string, string>;
105
+ declare const EN: Record<string, string>;
106
+ declare function fallbackTranslate(key: string, params?: Record<string, string | number>): string;