@pure01fx/dsh-openai-codex-auth 0.8.0 → 0.10.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.10.0
6
+
7
+ ### Codex cloud tools
8
+
9
+ - Adds optional codex_web, codex_image_generate, codex_image_edit and codex_image_inspect tools using the current Codex OAuth account and public DSH services.
10
+ - Adds account/session-bound search references and visible context, original image artifacts with normalized previews, bounded multi-image edits and explicit vision precision/usage. Nonvisual callers keep textual results and canonical attachment references.
11
+ - Tracks cloud service request contracts at Codex b348fc26674189f758d5941cdab3f78f258b2aa7. Live account availability is tracked separately in CODEX-COMPATIBILITY.md; realtime voice is excluded.
12
+
13
+ ## 0.9.0
14
+
15
+ ### Codex 0.153 compatibility
16
+
17
+ - Tracks Codex main snapshot `ddf04ad` and uses the stable `0.153.4` model-catalog client version after auditing the intervening Responses, WebSocket, OAuth, Catalog, and quota protocol changes.
18
+ - Preserves the complete bounded `response.completed.response.usage` object alongside the exact high-precision `usage_metadata.amount` value across HTTP/SSE, WebSocket, and same-origin status responses.
19
+ - Supports GPT-6-Astra and its Fast alias through the complete Responses Lite contract: stable developer instruction/tool prefix items, namespaced functions, all-turn reasoning context, model-default verbosity, disabled parallel tool calls, the HTTP Lite header, and the WebSocket request-metadata route marker. Standard Responses requests remain unchanged.
20
+ - Recognizes `normal_model_slug` on additional `wham/usage` limits and honors the backend's `allowed` decision when `limit_reached` is absent, while intentionally remaining a passive client without the Luna Reserve opt-in header.
21
+ - Shows an isolated quota snapshot, including remaining reset credits, for every managed account, refreshing once when Codex settings opens or on explicit request; concurrent usage lookups are time-bounded outside the main credential lock, rotating refresh operations are journaled, bounded independently of caller cancellation, single-flighted per account across service processes, recoverable after persistence contention, and never ambiguously retried, stale non-current credentials are renewed without changing the selected account, and one account's failure does not hide the others.
22
+
5
23
  ## 0.8.0
6
24
 
7
25
  ### Multi-account authentication
@@ -0,0 +1,93 @@
1
+ # Codex 云端能力兼容性跟踪
2
+
3
+ ## 当前实现与验证边界
4
+
5
+ 本次范围为已确认的非语音 Codex 云端服务。参考上游 b348fc26674189f758d5941cdab3f78f258b2aa7(cloud 专用跟踪常量在 src/upstream.ts);原 Provider 协议基线仍单独保留。四个工具是普通 DSH function 工具,不依赖专用模式,不覆盖现有工具。
6
+
7
+ | 能力 | 当前实现 | 验证与来源 |
8
+ | --- | --- | --- |
9
+ | 文本/推理、图像输入 | 保留 Standard/Lite 和 HTTP/WebSocket | 原有回归;responses.ts、native-http.ts |
10
+ | 网页/图片搜索与浏览查询 | codex_web 覆盖十种 commands、模式/过滤/位置/上下文;账号/会话绑定引用 | cloud-http/search/context/web-tool 及真实 ToolRuntime mock 集成;上游 codex-api/src/search.rs、endpoint/search.rs |
11
+ | 搜索图片、PDF 截图 | 已知内联 input_image 进入附件;远程 URL 保留链接和不可提取说明,未知 DTO 保留有界 opaque results | 上游 protocol/src/models.rs;公开 DSH web 暂无二进制下载边界;未把链接等同已下载图片 |
12
+ | 图像生成、编辑 | JSON generations/edits,全部 1–4 输出;1–5 输入;原图与预览分开 | cloud-images/media/tools;实际 LocalAttachmentStore + sharp 解码/EXIF/字节保真测试;上游 codex-api/src/images.rs、endpoint/images.rs |
13
+ | 显式识图 | 无递归工具的单次 Responses/Lite;original/high 回退、Lite 省略,实际尺寸与 usage | cloud-vision/tools 与流/注册测试;上游 core/src/client_common.rs、protocol/src/openai_models.rs |
14
+ | 实时语音 | 按用户要求排除 | 不注册语音工具,不宣称支持 |
15
+
16
+ 生成/编辑原图仅通过调用者公开 fs/shell/sandboxPolicy 保存到工作区 .dsh/codex-images;附件通过公开 AttachmentStore 完整解码。测试包括实际文件写入及权限参数传递,mock 参数测试不等于真实沙箱强制验证。Code Mode 同一程序内已返回图片有短期会话缓存,历史变动后依赖已归档可见附件;不会接受其他会话的任意 ID。
17
+
18
+ 真实账号验证(2026-09-10):网页搜索、基于返回 ref 的 open、图片搜索、一次低质量单图生成、一次对生成图编辑、gpt-5.5 original 识图均成功。共 8/12 次请求(包括一次目录请求、一次图片搜索结构核对),生成/编辑各仅一次。图片搜索返回文本中的 11 个图片引用和 22 个 URL,没有二进制附件;保留这些来源不等于内联图片已下载。生成原图实际为 1254×1254(服务端未严格采用请求的 1024×1024),识图按该实际尺寸发送;返回 usage 为 inputTokens=1956、outputTokens=19。只记录摘要与 SHA-256,不保存账号、token 或完整图片/提示。
19
+
20
+ 本地 pnpm build、22 文件/365 项测试、pnpm pack 与 git diff --check 均通过;安装包包含八个新模块与声明。详细不可变 evidence、脱敏 live 结果和产物见同工作区 .runbook/runs/codex-cloud-capabilities。此结果证明本次当前账号的可用性,不保证所有账号和未来内部 endpoint。当前常驻 GUI 未重启,也未安装替换;详细用法见 README 的“云端搜索和图片工具”。
21
+
22
+ ## 初次广义审查(历史记录,以下状态与建议不再代表当前实现或执行范围)
23
+
24
+ ## 本次核对基线
25
+
26
+ - 插件版本:0.9.0。
27
+ - 插件声明跟踪:`ddf04ad26789d040f9ef6a96736f76602e35a6cc`(`src/upstream.ts:3`)。
28
+ - 本地 `../codex` 已 fast-forward 至 `b348fc26674189f758d5941cdab3f78f258b2aa7`,与本次 fetch 的 `origin/main` 一致,增加 272 个提交,工作树干净。
29
+ - 原 checkout 为 sparse checkout;为核对完整工具类型,追加了 `codex-rs/tools`。
30
+ - 验证:`pnpm test`,14 个测试文件、233 项测试全部通过,包括生成产物与新鲜 TypeScript 编译一致性检查。未使用真实账号执行新增能力的端到端验证。
31
+ - 本次是更新上游后的差异检查;没有提升插件的 TRACKED_CODEX_COMMIT,也没有宣称已完成新版本兼容。
32
+
33
+ ## 结论
34
+
35
+ 当前插件能够传送 DSH 注册的普通 JSON function 工具,但不能承诺具备 Codex 的完整工具能力。上游 `codex-rs/tools/src/tool_spec.rs:22` 声明 function、namespace、tool_search、web_search、custom 五种协议类型;插件的 `src/responses.ts:69` 只生成 function,Lite 路径只额外包装固定的 functions namespace。这几种工具声明在本次 272 个提交之前就已存在:该区间没有修改上游 tool_spec.rs。
36
+
37
+ Codex 本地工具由其 runtime 执行;OAuth 登录和 Responses 适配不会自动安装这些执行器。DSH 可以提供类似功能,但必须分别核对参数、执行、结果、恢复和权限语义,不能仅按工具名称判断一致。上游工具也受模型、配置、平台、功能开关和账号可用性约束,并非每个会话全部启用。
38
+
39
+ ## 协议能力清单
40
+
41
+ | 能力 | 当前状态与证据 | 补齐方向 |
42
+ | --- | --- | --- |
43
+ | JSON function 调用、参数流、工具结果 | 已实现;`src/responses.ts:69`、`:503` | 保持请求→流解析→执行→结果→续轮覆盖 |
44
+ | 图片输入及图片工具结果 | 已实现;`src/responses.ts:50`、`:57` | 仍缺上游 image detail 选择,不能等同完整图像能力 |
45
+ | Freeform / grammar / custom | 缺失;上游 `core/src/tools/handlers/apply_patch_spec.rs:18` 和 `core/src/tools/code_mode/execute_spec.rs:26` 使用 custom;插件不会声明此类型,`:593` 拒绝其 completed item | 扩展工具 schema、原始字符串参数、custom_tool_call/output、流和 replay,并接实际执行器 |
46
+ | 通用 namespace | 部分支持;Lite 固定 functions;`:526` 保存返回 namespace,但 `:515`/`:521` 向执行层只输出裸 name | 保留 namespace 身份和描述,支持同名工具隔离及普通/Lite 请求 |
47
+ | 延迟发现 tool_search | 缺失原生协议;上游 `core/src/tools/handlers/tool_search_spec.rs:97` 使用 execution=client | 实现 defer_loading、搜索/加载结果、additional_tools、后续工具可见性;普通同名 function 不等同此协议 |
48
+ | OpenAI 托管 web_search | 缺失;上游 `core/src/tools/hosted_spec.rs:14` 配置 cached/indexed/live、域名/位置/内容类型;插件只声明 functions,拒绝 web_search_call completed item | 显式配置托管工具、事件、引用/来源和续轮;DSH 自有搜索仅为另一种实现 |
49
+ | 消息 phase 和扩展元数据 | 未完整保存;`src/replay.ts:10` 的 message 只有 id/blocks;上游 `protocol/src/models.rs:1008` 包含 phase 和 internal_chat_message_metadata_passthrough | 有界保留所需字段并明确 UI 与历史恢复语义 |
50
+ | 其他 response items | 插件仅接受 function_call/message/reasoning,其余在 `src/responses.ts:593` 报 UNSUPPORTED | 按实际声明的能力逐类实现,不能仅忽略未知项假装支持 |
51
+ | HTTP/SSE、WebSocket v2、加密 reasoning replay | 已有实现和测试(native-http、native-websocket、replay suites) | 现有测试通过不代表新增工具类型已覆盖 |
52
+
53
+ 上游路径在表中均相对于 `../codex/codex-rs/`。
54
+
55
+ ## DSH 宿主边界
56
+
57
+ 已检查当前安装的 `@deepseek-ai/dsh-llm/lib/types/types.d.ts:325`:ToolSchema 仅有 name、description、parameters;GenerateOptions.tools 是该类型数组。通用 namespace、freeform、托管工具和 deferred loading 没有直接表达位置。因此完整支持需要 DSH 的公共工具/LLM 类型与执行链配合,或先设计明确的 provider 扩展接口;只修改此插件的一处 tools JSON 不足以完成。
58
+
59
+ 需另外核对的执行能力:
60
+
61
+ - Codex exec_command/write_stdin 的交互进程、PTY、环境选择,与 DSH bash/job 工具的差异。
62
+ - apply_patch 的 grammar 和补丁执行,与 DSH edit/write 的差异。
63
+ - Code Mode 的原始代码、yield/wait、工具调用追踪与 DSH run_code 的差异。
64
+ - MCP/Apps 的发现、namespace、授权、工具结果元数据与 DSH MCP 插件的差异。
65
+ - 多 agent、异步用户输入、上下文切换/余量工具、图像生成等是否由目标 DSH profile 配齐。
66
+
67
+ 这些项目尚未逐项做运行时等价验证,不能从本次协议审查推出缺失于所有 DSH profile,亦不能标记为已对齐。上游注册入口见 `core/src/tools/spec_plan.rs`。
68
+
69
+ ## 本次上游增量中相关变化
70
+
71
+ - `core/src/client_common.rs` 新增基于模型能力的 image detail 规范化:Lite 清除 detail;不支持 original 的普通模型回退默认 detail。插件当前统一不发送 detail,未表达该能力。
72
+ - `protocol/src/models/executed_tool_calls.rs` 扩展 tool_result_metadata;插件没有对应执行追踪元数据。
73
+ - `protocol/src/openai_models/reasoning_effort.rs` 将 Ultra/Persistent 归一化集中到模型类型;插件已实现相似映射,需继续用 fixtures 校验,不能把源码重构直接认定成缺功能。
74
+ - `codex-api` 删除旧 compaction endpoint 实现,不能默认把旧 /compact 接入方式作为升级方案;DSH 当前使用辅助生成压缩,属于单独的运行时策略。
75
+
76
+ ## 测试盲区与后续复现
77
+
78
+ 现有测试集中于 function 调用。独立源码复核还发现以下待复现边界,尚不作为已验证的线上故障:
79
+
80
+ - `src/responses.ts:597` 不从 response.completed.response.output 提取输出,仅依赖前面的流事件;需确认目标服务是否可能只发送终态内嵌输出。
81
+ - 只有 encrypted reasoning 且 summary 为空时,当前没有可见 block,会走 EMPTY_RESPONSE;现有相关测试仅检查 finish 存在,未验证成功/错误语义。
82
+ - 重复 item.added/item.done 缺少明确重复调用防护;需用真实结构的事件回放验证是否可能重复分发。
83
+ - namespace 冲突、custom/builtin completed item、refusal 内容、复杂 schema 等缺少对应的兼容性行为覆盖。
84
+
85
+ ## 建议推进与验收
86
+
87
+ 1. 优先处理现有支持路径的保真性:phase、namespace 路由身份、相关 replay 元数据;增加 HTTP/SSE 与 WebSocket 的多轮恢复用例。
88
+ 2. 设计宿主工具 schema 扩展,接通 custom/freeform 和通用 namespace;以真实 apply_patch/code-mode 调用、同名 namespace 隔离及重启恢复作验收。
89
+ 3. 接通工具发现与托管 web_search;验证延迟加载、引用/来源、失败/取消和后续轮次。
90
+ 4. 对目标 DSH profile 建立执行能力矩阵,再处理交互 shell、MCP/Apps、多 agent、图像生成等 runtime 项。
91
+ 5. 新增上游 fixture/能力矩阵检查;每次升级记录审查过的 commit 和尚未支持项,行为测试通过后再提升 src/upstream.ts 的兼容基线。
92
+
93
+ 测试应覆盖有效请求和两种传输下的完整调用生命周期,而不仅是静态工具名称清单。未知能力仍应明确报错,不能丢弃事件后把测试标记为兼容。
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  ## 快速开始
19
19
 
20
- 当前 `0.7.x` 版本要求 DeepSeek Harness `0.1.1-rc.2`;仍运行 DSH `0.1.0-rc.6` 的 profile 应继续使用插件 `0.6.1`。
20
+ 当前 `0.10.0` 版本要求 DeepSeek Harness `0.1.1-rc.2`;仍运行 DSH `0.1.0-rc.6` 的 profile 应继续使用插件 `0.6.1`。
21
21
 
22
22
  将插件安装到 DSH 的 `web` profile:
23
23
 
@@ -48,6 +48,65 @@ dsh web
48
48
 
49
49
  设备码登录无需 OAuth callback,适合本机、SSH 隧道和 HTTPS 反向代理场景。
50
50
 
51
+ ## 云端搜索和图片工具
52
+
53
+ 插件通过当前 Codex OAuth 账号提供以下普通 DSH 工具,支持 native 和 Code Mode。无需切换专用 preset;需要该 preset 已提供公开的 tools 服务。服务端是否开放能力仍取决于账号和模型。
54
+
55
+ | 工具 | 用途 | 关键参数 |
56
+ | --- | --- | --- |
57
+ | `codex_web` | 网页/图片搜索、打开、点击、查找、PDF 截图、金融、天气、体育和时间 | `commands` 必填;`model/context/mode/search_context_size/user_location/filters/image_settings` 可选 |
58
+ | `codex_image_generate` | 文生图,保存每张原图及预览附件 | `prompt` 必填;`model/quality/background/size/n` 可选 |
59
+ | `codex_image_edit` | 编辑 1–5 张图片 | `prompt` 必填;`images` 或 `num_last_images_to_include` 二选一;其他参数同生成 |
60
+ | `codex_image_inspect` | OCR、截图/图表解释及普通识图 | `prompt/images` 必填;`model/detail/reasoning_effort` 可选 |
61
+
62
+ ```yaml
63
+ cloudTools:
64
+ enabled: true
65
+ search: true
66
+ imageGeneration: true
67
+ imageEditing: true
68
+ imageInspection: true
69
+ # searchModel: <你的 Codex 目录中的模型 ID>
70
+ # visionModel: <你的 Codex 目录中支持图片的模型 ID>
71
+ imageModel: gpt-image-2
72
+ searchMode: live # cached | indexed | live
73
+ visionDetail: auto # auto | low | high | original
74
+ ```
75
+
76
+ 搜索和识图依次选择显式 `model`、对应配置、当前 Codex 模型。使用 DeepSeek 等其他模型作为主模型时,需要明确配置 Codex 的 searchModel/visionModel;不会把主模型的名称发给 Codex。生成和编辑默认使用 gpt-image-2,与主模型无关。只需 OAuth/Provider 时可设置 `cloudTools.enabled: false`;缺失工具服务不会影响登录与文本模型。
77
+
78
+ 调用示例(Code Mode 的 `tools.*` 接口):
79
+
80
+ ```ts
81
+ const found = await tools.codex_web({
82
+ commands: { search_query: [{ q: 'OpenAI Codex documentation' }], response_length: 'short' },
83
+ })
84
+ console.log(found)
85
+ // 后续 open/click/find 使用搜索结果中的 ref_id;也可以 open 一个 URL。
86
+
87
+ const created = await tools.codex_image_generate({
88
+ prompt: 'A simple red circle on a white background', size: '1024x1024', n: 1,
89
+ })
90
+ console.log(created)
91
+ // 下一次调用可指定原图 path,或 images:[{attachment_id:附件ID}]。
92
+ const edited = await tools.codex_image_edit({
93
+ prompt: 'Change the circle to blue', num_last_images_to_include: 1,
94
+ })
95
+ console.log(edited)
96
+ ```
97
+
98
+ 搜索的 commands 支持 `search_query/image_query/open/click/find/screenshot/finance/weather/sports/time`,至少提供一个非空操作数组;response_length 不能单独调用。搜索默认只发送最近两条用户文本及其间最多 1000 UTF-8 字节的 assistant 文本(保守小于等于 token 上限),显式 context 可覆盖;不会发送系统提示、隐藏推理或工具私有上下文。引用按账号和会话隔离,切换账号后请重新搜索。结果保留正文、来源 URL、有界 opaque results 和引用;截断会明确注明。
99
+
100
+ 可识别的内联图片结果进入 DSH 附件。当前公开 web 服务只返回 HTML/文本,远程图片/PDF 截图 URL 会保留为链接并注明无法提取图片;不会把 OAuth 发往这些 URL。未知图片结构保留为有界结果,不能把链接展示当成图片已下载。主模型不支持图片时保留附件元数据与文字,不向该模型发送 image block。
101
+
102
+ 图片工具需要调用者的 fs、attachments 服务;生成/编辑的原图保存还需要执行文件权限的 shell 和 sandboxPolicy。原图写入调用者工作区的 `.dsh/codex-images/`,采用独占创建、0600 权限并返回可读路径。预览可能经过缩放、转码或 EXIF 旋转,原图与预览尺寸分别记录。附件 ID 必须来自当前可见会话或本次程序已返回的图片;绝对路径按调用者文件权限读取。编辑默认不是 multipart,不支持未声明的 mask/seed 字段。
103
+
104
+ 生成/编辑默认 quality、background、size 均为 auto,n 缺省不发送;一次最多返回 4 张图,输入最多 5 张,聚合图片字节有上限。服务拒绝、不确定网络失败或本地保存失败不会触发自动重复生成。401 仅允许同账号刷新后重试一次;不自动切换账号。
105
+
106
+ 识图是额外的一次无工具 Responses/Responses Lite 调用,返回文本、用量及实际发送图片尺寸。支持 original 的普通模型会保留原文件编码数据;不支持时降为 high,Lite 省略 detail。只提供缩放附件不能恢复原始像素。普通会话附图依旧直接发给主模型,不会隐式加一次识图调用。实时语音不在此实现范围内。
107
+
108
+ 详细源码依据、验证状态和限制见 [CODEX-COMPATIBILITY.md](./CODEX-COMPATIBILITY.md)。
109
+
51
110
  ## 多账号切换
52
111
 
53
112
  - 已登录后继续点击 **用设备码添加账号** 或 **用浏览器添加账号**,可追加 ChatGPT 账号;新登录的账号会自动成为当前账号。
package/client.js CHANGED
@@ -181,6 +181,7 @@ window.__ModuleLoader__.load({
181
181
  .codexButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l3)}.codexButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.codexButton:disabled{color:var(--dsw-alias-label-dimmed);cursor:not-allowed}
182
182
  .codexButton.primary{border-color:transparent;background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.codexButton.primary:hover:not(:disabled){border-color:transparent;background:var(--dsw-alias-button-primary-hover)}.codexButton.danger{color:var(--dsw-alias-state-error-primary)}
183
183
  .codexGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:14px}
184
+ .codexAccounts{margin-top:14px;margin-bottom:20px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:14px}.codexAccountList{display:flex;flex-direction:column;gap:8px}.codexAccountOption{display:flex;align-items:center;justify-content:space-between;gap:18px;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-layer-2)}.codexAccountDetails{flex:1;min-width:0}.codexAccountTitle{display:flex;align-items:center;flex-wrap:wrap;gap:6px 10px;min-width:0}.codexAccountTitle strong{font-size:13px;font-weight:500;overflow-wrap:anywhere}.codexAccountTitle .codexBadge{padding:1px 8px;line-height:18px}.codexAccountMeta{display:flex;flex-wrap:wrap;gap:2px 14px;margin-top:4px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.codexAccountActions{flex:none;flex-wrap:nowrap;margin-top:0}.codexAccountActions .codexButton{white-space:nowrap}.codexAccountHeaderActions{display:flex;align-items:center;gap:10px}.codexAccountRefresh{min-height:28px;padding:3px 9px;font-size:12px}.codexAccountQuota{display:flex;flex-wrap:wrap;gap:5px 14px;margin-top:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:9px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.codexAccountQuotaItem{white-space:nowrap}.codexAccountQuotaItem strong{color:var(--dsw-alias-label-primary);font-weight:500;font-variant-numeric:tabular-nums}.codexAccountQuotaStatus{color:var(--dsw-alias-label-tertiary)}.codexAccountQuotaStatus.error{color:var(--dsw-alias-state-error-primary)}
184
185
  .codexUsage{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-layer-2)}
185
186
  .codexUsageHead{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.codexUsageName{font-size:13px;font-weight:500}.codexUsageValue{font-size:12px;color:var(--dsw-alias-label-tertiary)}
186
187
  .codexBar{height:6px;margin:10px 0 8px;overflow:hidden;border-radius:999px;background:var(--dsw-alias-bg-layer-1)}.codexBarFill{height:100%;border-radius:inherit;background:var(--dsw-alias-state-success-primary);transition:width .25s ease}.codexBarFill.high{background:var(--dsw-alias-state-warn-label)}
@@ -203,7 +204,7 @@ window.__ModuleLoader__.load({
203
204
  .codexQuotaTrack{fill:none;stroke:var(--dsw-alias-border-l3);stroke-width:2px}.codexQuotaAvailable{fill:none;stroke:var(--dsw-alias-state-success-primary);stroke-width:2px;stroke-linecap:round;transition:stroke .25s ease,stroke-dasharray .25s ease,stroke-dashoffset .25s ease}.codexQuotaAvailable.high{stroke:var(--dsw-alias-state-warn-label)}.codexQuotaAvailable.critical{stroke:var(--dsw-alias-state-error-primary)}.codexQuotaLoading{animation:codexQuotaPulse .8s ease-in-out infinite alternate}
204
205
  .codexQuotaPanel{z-index:100;box-sizing:border-box;width:272px;border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;padding:12px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-secondary);cursor:default;font-size:12px;line-height:20px;position:absolute;right:0;bottom:calc(100% + 8px)}.codexQuotaPanelHead{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:8px}.codexQuotaPanelHead strong{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500}.codexQuotaPanelHead span{color:var(--dsw-alias-label-tertiary)}
205
206
  .codexQuotaWindow{padding:7px 0}.codexQuotaWindow+.codexQuotaWindow{border-top:1px solid var(--dsw-alias-border-l2)}.codexQuotaWindowHead{display:flex;align-items:center;justify-content:space-between;gap:10px}.codexQuotaWindowHead strong{font-weight:500;color:var(--dsw-alias-label-secondary)}.codexQuotaWindowHead span{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary)}.codexQuotaBar{height:4px;margin:6px 0;border-radius:999px;overflow:hidden;background:var(--dsw-alias-interactive-bg-hover)}.codexQuotaBarFill{height:100%;border-radius:inherit;background:var(--dsw-alias-state-success-primary)}.codexQuotaBarFill.high{background:var(--dsw-alias-state-warn-label)}.codexQuotaBarFill.critical{background:var(--dsw-alias-state-error-primary)}.codexQuotaReset{color:var(--dsw-alias-label-tertiary)}.codexQuotaMessage{margin:4px 0;color:var(--dsw-alias-label-tertiary)}.codexQuotaMessage.error{color:var(--dsw-alias-state-error-primary)}
206
- @keyframes codexPulse{to{background-position:-200% 0}}@keyframes codexQuotaPulse{to{opacity:.35}}@media(prefers-reduced-motion:reduce){.codexSkeleton,.codexQuotaLoading{animation:none}}@media(max-width:620px){.codexHero{padding:14px;flex-direction:column}.codexBody{padding:14px}.codexGrid,.codexModelVisibilityList{grid-template-columns:1fr}.codexCode{font-size:20px}}
207
+ @keyframes codexPulse{to{background-position:-200% 0}}@keyframes codexQuotaPulse{to{opacity:.35}}@media(prefers-reduced-motion:reduce){.codexSkeleton,.codexQuotaLoading{animation:none}}@media(max-width:620px){.codexHero{padding:14px;flex-direction:column}.codexBody{padding:14px}.codexGrid,.codexModelVisibilityList{grid-template-columns:1fr}.codexAccountOption{align-items:stretch;flex-direction:column;gap:10px}.codexAccountActions{align-self:flex-start}.codexCode{font-size:20px}}
207
208
  `
208
209
  if (typeof document !== 'undefined' && document.querySelector('style[data-plugin="' + PLUGIN_ID + '"]') === null) {
209
210
  const style = document.createElement('style')
@@ -341,6 +342,26 @@ window.__ModuleLoader__.load({
341
342
  )
342
343
  }
343
344
 
345
+ function AccountQuotaSummary(props) {
346
+ const entry = props.entry
347
+ if (props.loading && !entry) return h('div', { className: 'codexAccountQuota' }, h('span', { className: 'codexAccountQuotaStatus' }, '正在读取额度…'))
348
+ if (!entry) return h('div', { className: 'codexAccountQuota' }, h('span', { className: 'codexAccountQuotaStatus' }, '尚未读取额度'))
349
+ if (entry.error) return h('div', { className: 'codexAccountQuota' }, h('span', { className: 'codexAccountQuotaStatus error', role: 'status' }, '额度读取失败:' + entry.error))
350
+ const usage = entry.usage
351
+ if (!usage) return h('div', { className: 'codexAccountQuota' }, h('span', { className: 'codexAccountQuotaStatus' }, '暂无额度数据'))
352
+ const rows = []
353
+ if (usage.planType) rows.push(h('span', { className: 'codexAccountQuotaItem', key: 'plan' }, String(usage.planType).toUpperCase()))
354
+ if (displayQuotaWindow(usage.primary)) rows.push(h('span', { className: 'codexAccountQuotaItem', key: 'primary' }, quotaWindowName(usage.primary, false) + ' ', h('strong', null, '剩余 ' + Math.max(0, Math.round(100 - usage.primary.usedPercent)) + '%')))
355
+ if (displayQuotaWindow(usage.secondary)) rows.push(h('span', { className: 'codexAccountQuotaItem', key: 'secondary' }, quotaWindowName(usage.secondary, true) + ' ', h('strong', null, '剩余 ' + Math.max(0, Math.round(100 - usage.secondary.usedPercent)) + '%')))
356
+ if (Number.isFinite(usage.resetCredits)) rows.push(h('span', { className: 'codexAccountQuotaItem', key: 'resetCredits' }, '额度重置 ', h('strong', null, '剩余 ' + Math.max(0, Math.floor(usage.resetCredits)) + ' 次')))
357
+ if (usage.credits) {
358
+ const credits = usage.credits.unlimited ? '无限' : usage.credits.hasCredits ? (usage.credits.balance || '可用') : '不可用'
359
+ rows.push(h('span', { className: 'codexAccountQuotaItem', key: 'credits' }, 'Credits ', h('strong', null, credits)))
360
+ }
361
+ if (usage.fetchedAt) rows.push(h('span', { className: 'codexAccountQuotaItem', key: 'updated' }, '更新 ' + new Date(usage.fetchedAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })))
362
+ return h('div', { className: 'codexAccountQuota' }, rows.length > 0 ? rows : h('span', { className: 'codexAccountQuotaStatus' }, '暂无额度数据'))
363
+ }
364
+
344
365
  function quotaTone(used) {
345
366
  return used >= 95 ? ' critical' : used >= 80 ? ' high' : ''
346
367
  }
@@ -484,7 +505,13 @@ window.__ModuleLoader__.load({
484
505
  const [manualInput, setManualInput] = useState('')
485
506
  const [now, setNow] = useState(Date.now())
486
507
  const [modelVisibility, setModelVisibility] = useState(readModelVisibility)
508
+ const [accountUsages, setAccountUsages] = useState({})
509
+ const [accountUsageBusy, setAccountUsageBusy] = useState(false)
510
+ const [accountUsageError, setAccountUsageError] = useState('')
511
+ const [accountStatusSyncError, setAccountStatusSyncError] = useState('')
487
512
  const probeGeneration = useRef(0)
513
+ const accountUsageGeneration = useRef(0)
514
+ const accountUsageScope = useRef('')
488
515
 
489
516
  useEffect(() => {
490
517
  const reload = () => { setModelVisibility(readModelVisibility()) }
@@ -566,6 +593,52 @@ window.__ModuleLoader__.load({
566
593
  return value
567
594
  }, [status])
568
595
 
596
+ const refreshAccountUsages = useCallback(async () => {
597
+ const generation = ++accountUsageGeneration.current
598
+ setAccountUsageBusy(true)
599
+ setAccountUsageError('')
600
+ setAccountStatusSyncError('')
601
+ try {
602
+ const value = await post('/accounts/usage')
603
+ const next = {}
604
+ for (const row of Array.isArray(value.accounts) ? value.accounts : []) {
605
+ if (row && typeof row.accountId === 'string') next[row.accountId] = row
606
+ }
607
+ if (generation === accountUsageGeneration.current) {
608
+ setAccountUsages(next)
609
+ try {
610
+ await requestStatus(false)
611
+ } catch (statusError) {
612
+ if (generation === accountUsageGeneration.current) {
613
+ setAccountStatusSyncError('额度已刷新,但账号状态同步失败:' + messageOf(statusError))
614
+ }
615
+ }
616
+ }
617
+ } catch (usageError) {
618
+ if (generation === accountUsageGeneration.current) setAccountUsageError(messageOf(usageError))
619
+ } finally {
620
+ if (generation === accountUsageGeneration.current) setAccountUsageBusy(false)
621
+ }
622
+ }, [post])
623
+
624
+ const accountUsageIds = status && Array.isArray(status.accounts)
625
+ ? status.accounts.map(account => account.accountId).filter(Boolean).sort().join('\n')
626
+ : ''
627
+ useEffect(() => {
628
+ if (!status || !status.csrf || !accountUsageIds) {
629
+ if (accountUsageScope.current) {
630
+ accountUsageScope.current = ''
631
+ setAccountUsages({})
632
+ setAccountUsageError('')
633
+ setAccountStatusSyncError('')
634
+ }
635
+ return
636
+ }
637
+ if (accountUsageScope.current === accountUsageIds) return
638
+ accountUsageScope.current = accountUsageIds
639
+ void refreshAccountUsages()
640
+ }, [accountUsageIds, refreshAccountUsages, status && status.csrf])
641
+
569
642
  const startDevice = async () => {
570
643
  setBusy('device')
571
644
  setCopied(false)
@@ -787,23 +860,33 @@ window.__ModuleLoader__.load({
787
860
  )
788
861
  : null,
789
862
  !loading && accounts.length > 0
790
- ? h('div', { className: 'codexModelVisibility' },
863
+ ? h('div', { className: 'codexAccounts' },
791
864
  h('div', { className: 'codexModelVisibilityHead' },
792
865
  h('strong', null, '已添加账号'),
793
- h('span', { className: 'codexMeta' }, accounts.length + ' 个'),
866
+ h('div', { className: 'codexAccountHeaderActions' },
867
+ h('span', { className: 'codexMeta' }, accounts.length + ' 个'),
868
+ h('button', { type: 'button', className: 'codexButton codexAccountRefresh', disabled: accountUsageBusy, onClick: () => { void refreshAccountUsages() } }, accountUsageBusy ? '刷新中…' : '刷新额度'),
869
+ ),
794
870
  ),
795
- h('p', { className: 'codexModelVisibilityHint' }, '当前账号用于新的 Codex 请求;你可以切换或单独移除任意账号。'),
796
- h('div', { className: 'codexModelVisibilityList' }, accounts.map((account) => {
871
+ h('p', { className: 'codexModelVisibilityHint' }, '当前账号用于新的 Codex 请求;每个账号的额度仅在打开本页或点击刷新时读取。'),
872
+ accountUsageError ? h('p', { className: 'codexAccountQuotaStatus error', role: 'status' }, '额度刷新失败:' + accountUsageError) : null,
873
+ accountStatusSyncError ? h('p', { className: 'codexAccountQuotaStatus error', role: 'status' }, accountStatusSyncError) : null,
874
+ h('div', { className: 'codexAccountList' }, accounts.map((account) => {
797
875
  const isCurrent = account.current === true || account.accountId === currentAccountId
798
876
  const accountBusy = busy === 'current:' + account.accountId || busy === 'remove:' + account.accountId
799
- return h('div', { className: 'codexModelVisibilityOption', key: account.accountId },
800
- h('span', { title: account.accountId },
801
- h('strong', null, account.email || shortAccount(account.accountId)),
802
- account.email ? h('span', null, ' · ' + shortAccount(account.accountId)) : null,
803
- h('span', null, isCurrent ? ' · 当前账号' : ''),
804
- account.expiresAt ? h('span', null, ' · 到期 ' + new Date(account.expiresAt).toLocaleString()) : null,
877
+ return h('div', { className: 'codexAccountOption', key: account.accountId },
878
+ h('div', { className: 'codexAccountDetails', title: account.accountId },
879
+ h('div', { className: 'codexAccountTitle' },
880
+ h('strong', null, account.email || shortAccount(account.accountId)),
881
+ isCurrent ? h('span', { className: 'codexBadge connected' }, '当前账号') : null,
882
+ ),
883
+ h('div', { className: 'codexAccountMeta' },
884
+ account.email ? h('span', null, shortAccount(account.accountId)) : null,
885
+ account.expiresAt ? h('span', null, '到期 ' + new Date(account.expiresAt).toLocaleString()) : null,
886
+ ),
887
+ h(AccountQuotaSummary, { entry: accountUsages[account.accountId], loading: accountUsageBusy }),
805
888
  ),
806
- h('div', { className: 'codexActions' },
889
+ h('div', { className: 'codexActions codexAccountActions' },
807
890
  !isCurrent ? h('button', { type: 'button', className: 'codexButton', disabled: Boolean(busy), onClick: () => { void setCurrentAccount(account.accountId) } }, accountBusy && busy.startsWith('current:') ? '切换中…' : '设为当前') : null,
808
891
  h('button', { type: 'button', className: 'codexButton danger', disabled: Boolean(busy), onClick: () => { void removeAccount(account.accountId) } }, accountBusy && busy.startsWith('remove:') ? '移除中…' : '移除'),
809
892
  ),
package/lib/catalog.d.ts CHANGED
@@ -31,8 +31,14 @@ export interface NativeCodexModel {
31
31
  additionalSpeedTiers: readonly string[];
32
32
  serviceTiers: readonly NativeCodexServiceTier[];
33
33
  defaultServiceTier?: string;
34
+ /** Requires the model-specific Responses Lite wire contract, not Standard Responses. */
35
+ useResponsesLite?: boolean;
36
+ /** Catalog-provided defaults needed to construct the model-specific request. */
37
+ defaultVerbosity?: string;
38
+ instructionsTemplate?: string;
34
39
  contextWindow?: number;
35
40
  inputModalities: readonly string[];
41
+ supportsImageDetailOriginal?: boolean;
36
42
  }
37
43
  export interface NativeCodexCatalogView {
38
44
  models: readonly NativeCodexModel[];
package/lib/catalog.js CHANGED
@@ -128,6 +128,7 @@ function parseModel(value) {
128
128
  const additionalSpeedTiers = strictStringList(row?.additional_speed_tiers);
129
129
  const serviceTiers = parseServiceTiers(row?.service_tiers);
130
130
  const inputModalities = strictStringList(row?.input_modalities, ['text', 'image']);
131
+ const useResponsesLite = row?.use_responses_lite ?? false;
131
132
  if (slug === undefined
132
133
  || displayName === undefined
133
134
  || reasoning === undefined
@@ -138,6 +139,7 @@ function parseModel(value) {
138
139
  || priority < -2_147_483_648 || priority > 2_147_483_647
139
140
  || additionalSpeedTiers === undefined
140
141
  || serviceTiers === undefined
142
+ || typeof useResponsesLite !== 'boolean'
141
143
  || inputModalities === undefined
142
144
  || !inputModalities.every(value => ['text', 'image', 'audio'].includes(value))) {
143
145
  throw invalidCatalog('native Codex catalog contained an invalid model entry');
@@ -160,15 +162,32 @@ function parseModel(value) {
160
162
  && nonEmptyString(row.multi_agent_reasoning_effort) === undefined) {
161
163
  throw invalidCatalog('native Codex catalog contained an invalid multi-agent reasoning effort');
162
164
  }
165
+ if (row.default_verbosity !== undefined && row.default_verbosity !== null
166
+ && nonEmptyString(row.default_verbosity) === undefined) {
167
+ throw invalidCatalog('native Codex catalog contained an invalid default verbosity');
168
+ }
169
+ const modelMessages = row.model_messages === undefined || row.model_messages === null
170
+ ? undefined : record(row.model_messages);
171
+ if ((row.model_messages !== undefined && row.model_messages !== null && modelMessages === undefined)
172
+ || (modelMessages?.instructions_template !== undefined
173
+ && modelMessages.instructions_template !== null
174
+ && nonEmptyString(modelMessages.instructions_template) === undefined)) {
175
+ throw invalidCatalog('native Codex catalog contained invalid model instructions');
176
+ }
163
177
  for (const key of ['context_window', 'max_context_window']) {
164
178
  if (row[key] !== undefined && row[key] !== null && positiveNumber(row[key]) === undefined) {
165
179
  throw invalidCatalog('native Codex catalog contained an invalid context window');
166
180
  }
167
181
  }
182
+ if (row.supports_image_detail_original !== undefined && typeof row.supports_image_detail_original !== 'boolean') {
183
+ throw invalidCatalog('native Codex catalog contained invalid original image capability');
184
+ }
168
185
  const description = nonEmptyString(row.description);
169
186
  const defaultReasoningLevel = nonEmptyString(row.default_reasoning_level);
170
187
  const multiAgentReasoningEffort = nonEmptyString(row.multi_agent_reasoning_effort);
171
188
  const defaultServiceTier = nonEmptyString(row.default_service_tier);
189
+ const defaultVerbosity = nonEmptyString(row.default_verbosity);
190
+ const instructionsTemplate = nonEmptyString(modelMessages?.instructions_template);
172
191
  const contextWindow = positiveNumber(row.context_window) ?? positiveNumber(row.max_context_window);
173
192
  return {
174
193
  slug,
@@ -183,8 +202,13 @@ function parseModel(value) {
183
202
  additionalSpeedTiers,
184
203
  serviceTiers,
185
204
  ...defaultServiceTier === undefined ? {} : { defaultServiceTier },
205
+ useResponsesLite,
206
+ ...defaultVerbosity === undefined ? {} : { defaultVerbosity },
207
+ ...instructionsTemplate === undefined ? {} : { instructionsTemplate },
186
208
  ...contextWindow === undefined ? {} : { contextWindow },
187
209
  inputModalities,
210
+ ...row.supports_image_detail_original === undefined ? {}
211
+ : { supportsImageDetailOriginal: row.supports_image_detail_original },
188
212
  };
189
213
  }
190
214
  function parseModelsPayload(value) {
@@ -0,0 +1,20 @@
1
+ /** Caller-visible context only; never project reasoning, plugin instructions or replay state. */
2
+ import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
3
+ import { type Message } from '@deepseek-ai/dsh-llm';
4
+ import type { NativeCodexModel } from './catalog.js';
5
+ export declare function chooseCloudModel(params: {
6
+ requested?: string | undefined;
7
+ configured?: string | undefined;
8
+ currentProvider?: string | undefined;
9
+ currentModel?: string | undefined;
10
+ purpose: 'search' | 'vision';
11
+ }, models: readonly NativeCodexModel[]): NativeCodexModel;
12
+ /** Only results paired with a preceding call and their owning source can grant image visibility. */
13
+ export declare function visibleCloudImages(messages: readonly Message[]): ImageAttachmentRef[];
14
+ /** Last two human texts plus intervening visible assistant text. UTF-8 bytes conservatively
15
+ * upper-bound byte-level tokenizer tokens; a single 1000-byte assistant budget avoids
16
+ * relying on an unavailable model tokenizer. User text has a separate 64 KiB cap. */
17
+ export declare function recentSearchInput(messages: readonly Message[]): {
18
+ role: 'user' | 'assistant';
19
+ content: string;
20
+ }[];
@@ -0,0 +1,89 @@
1
+ import { LlmError } from '@deepseek-ai/dsh-llm';
2
+ export function chooseCloudModel(params, models) {
3
+ const selected = params.requested ?? params.configured ?? (params.currentProvider === 'openai-codex' ? params.currentModel : undefined);
4
+ if (!selected)
5
+ throw new LlmError('Specify model or configure cloudTools.' + (params.purpose === 'search' ? 'searchModel' : 'visionModel') + '; the current session must otherwise use openai-codex', 'MODEL_REQUIRED');
6
+ const model = models.find(item => item.slug === selected) ?? (selected.endsWith('-fast') ? models.find(item => item.slug === selected.slice(0, -5)) : undefined);
7
+ if (!model)
8
+ throw new LlmError('Selected Codex model is unavailable in the current account catalog', 'MODEL_NOT_FOUND');
9
+ if (params.purpose === 'vision' && !model.inputModalities.includes('image'))
10
+ throw new LlmError('Selected Codex model does not support image input', 'VISION_UNSUPPORTED');
11
+ return model;
12
+ }
13
+ /** Only results paired with a preceding call and their owning source can grant image visibility. */
14
+ export function visibleCloudImages(messages) {
15
+ const images = [];
16
+ const calls = new Set();
17
+ const visit = (blocks, depth = 0) => {
18
+ if (depth > 16)
19
+ return;
20
+ for (const block of blocks) {
21
+ if (block.type === 'image')
22
+ images.push(block.attachment);
23
+ else if (block.type === 'tool-result' && !block.isError && calls.has(block.toolCallId))
24
+ visit(block.content, depth + 1);
25
+ }
26
+ };
27
+ for (const message of messages) {
28
+ if (message.role === 'assistant') {
29
+ for (const block of message.content)
30
+ if (block.type === 'tool-call')
31
+ calls.add(block.id);
32
+ }
33
+ else if (message.role === 'user' && message.source.kind === 'user') {
34
+ // User messages grant only their direct images, never embedded purported tool results.
35
+ for (const block of message.content)
36
+ if (block.type === 'image')
37
+ images.push(block.attachment);
38
+ }
39
+ else if (message.role === 'user' && message.source.kind === 'tool' && calls.has(message.source.callId)) {
40
+ for (const block of message.content)
41
+ if (block.type === 'tool-result' && block.toolCallId === message.source.callId && !block.isError)
42
+ visit(block.content);
43
+ }
44
+ }
45
+ return images;
46
+ }
47
+ function boundedText(text, bytes, tail = false) {
48
+ const encoded = Buffer.from(text);
49
+ if (encoded.length <= bytes)
50
+ return text;
51
+ if (tail) {
52
+ let start = encoded.length - bytes;
53
+ while (start < encoded.length && (encoded[start] & 0xc0) === 0x80)
54
+ start++;
55
+ return encoded.subarray(start).toString('utf8');
56
+ }
57
+ let end = bytes;
58
+ while (end > 0 && (encoded[end] & 0xc0) === 0x80)
59
+ end--;
60
+ return encoded.subarray(0, end).toString('utf8');
61
+ }
62
+ /** Last two human texts plus intervening visible assistant text. UTF-8 bytes conservatively
63
+ * upper-bound byte-level tokenizer tokens; a single 1000-byte assistant budget avoids
64
+ * relying on an unavailable model tokenizer. User text has a separate 64 KiB cap. */
65
+ export function recentSearchInput(messages) {
66
+ const users = messages.map((message, i) => message.role === 'user' && message.source.kind === 'user' && message.content.some(block => block.type === 'text') ? i : -1).filter(i => i >= 0).slice(-2);
67
+ if (!users.length)
68
+ return [];
69
+ const selected = [];
70
+ let assistantBytes = 1000;
71
+ let userBytes = 64 * 1024;
72
+ // Budget newest text first, then restore chronology.
73
+ for (let i = messages.length - 1; i >= users[0]; i--) {
74
+ const message = messages[i];
75
+ const user = users.includes(i);
76
+ if (!user && (message.role !== 'assistant' || message.source.kind !== 'model'))
77
+ continue;
78
+ const text = message.content.filter(block => block.type === 'text').map(block => block.text).join('\n');
79
+ const content = boundedText(text, user ? userBytes : assistantBytes, true);
80
+ if (!content)
81
+ continue;
82
+ if (user)
83
+ userBytes -= Buffer.byteLength(content);
84
+ else
85
+ assistantBytes -= Buffer.byteLength(content);
86
+ selected.push({ role: user ? 'user' : 'assistant', content });
87
+ }
88
+ return selected.reverse();
89
+ }
@@ -0,0 +1,28 @@
1
+ import type { NativeCodexCredential } from './catalog.js';
2
+ export interface NativeCodexCloudOptions {
3
+ resolveCredential(signal?: AbortSignal): Promise<NativeCodexCredential>;
4
+ recoverCredential?(previous: NativeCodexCredential, signal?: AbortSignal): Promise<boolean>;
5
+ fetch?: typeof fetch;
6
+ endpoint?: string;
7
+ requestTimeoutMs?: number;
8
+ maxRequestBodyBytes?: number;
9
+ maxResponseBytes?: number;
10
+ }
11
+ export interface NativeCodexCloudPostOptions {
12
+ credential: NativeCodexCredential;
13
+ signal: AbortSignal;
14
+ headers?: Record<string, string>;
15
+ timeoutMs?: number;
16
+ maxResponseBytes?: number;
17
+ }
18
+ export type NativeCodexCloudPath = 'alpha/search' | 'images/generations' | 'images/edits';
19
+ export declare class NativeCodexCloudClient {
20
+ private readonly options;
21
+ private readonly endpoint;
22
+ constructor(options: NativeCodexCloudOptions);
23
+ resolveCredential(signal?: AbortSignal): Promise<NativeCodexCredential>;
24
+ post(path: NativeCodexCloudPath, body: Record<string, unknown>, options: NativeCodexCloudPostOptions): Promise<{
25
+ body: unknown;
26
+ requestId?: string;
27
+ }>;
28
+ }