dsh-codex-community 0.0.1

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 (53) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/CONTRIBUTING.en.md +54 -0
  3. package/CONTRIBUTING.md +54 -0
  4. package/LICENSE +201 -0
  5. package/NOTICE +12 -0
  6. package/README.en.md +98 -0
  7. package/README.md +98 -0
  8. package/SECURITY.md +41 -0
  9. package/SUPPORT.md +17 -0
  10. package/THIRD_PARTY_NOTICES.md +25 -0
  11. package/codex-community.patch.yml +13 -0
  12. package/dist/client/index.js +1037 -0
  13. package/dist/host/index.mjs +98 -0
  14. package/dist/internal/authorization-bridge.mjs +662 -0
  15. package/dist/internal/authorization-commit-tracker.mjs +49 -0
  16. package/dist/internal/codex-authorization.mjs +202 -0
  17. package/dist/internal/codex-credential-store.mjs +164 -0
  18. package/dist/internal/codex-identifiers.mjs +4 -0
  19. package/dist/internal/codex-pi-provider.mjs +137 -0
  20. package/dist/internal/codex-provider-runtime.mjs +256 -0
  21. package/dist/internal/codex-route-adapter.mjs +133 -0
  22. package/dist/internal/codex-session-resources.mjs +64 -0
  23. package/dist/internal/failure-normalizer.mjs +456 -0
  24. package/dist/internal/image-policy.mjs +45 -0
  25. package/dist/internal/quota-observer.mjs +142 -0
  26. package/dist/internal/reliability.mjs +12 -0
  27. package/dist/internal/remote-image-input.mjs +801 -0
  28. package/dist/internal/session-preference-command.mjs +52 -0
  29. package/dist/internal/session-preferences.mjs +93 -0
  30. package/dist/internal/stream-resilience.mjs +273 -0
  31. package/docs/README.en.md +15 -0
  32. package/docs/README.md +15 -0
  33. package/docs/architecture.en.md +106 -0
  34. package/docs/architecture.md +106 -0
  35. package/docs/compatibility.en.md +53 -0
  36. package/docs/compatibility.md +53 -0
  37. package/docs/configuration.en.md +61 -0
  38. package/docs/configuration.md +61 -0
  39. package/docs/contribution-sources.en.md +35 -0
  40. package/docs/contribution-sources.md +35 -0
  41. package/docs/github-about.md +15 -0
  42. package/docs/releases/v0.0.1.acceptance.json +160 -0
  43. package/docs/releases/v0.0.1.md +174 -0
  44. package/docs/releasing.en.md +290 -0
  45. package/docs/releasing.md +290 -0
  46. package/docs/testing.en.md +85 -0
  47. package/docs/testing.md +85 -0
  48. package/docs/troubleshooting.en.md +47 -0
  49. package/docs/troubleshooting.md +47 -0
  50. package/package.json +144 -0
  51. package/types/client.d.ts +160 -0
  52. package/types/index.d.ts +22 -0
  53. package/types/reliability.d.ts +78 -0
@@ -0,0 +1,174 @@
1
+ # v0.0.1 技术预览 / Technical Preview
2
+
3
+ > 发布日期 / Release date:2026-08-29
4
+ > 验收提交 / Accepted commit:5d1cedeb3288d22ee2994f0908902e7b825c6704
5
+ > npm:`dsh-codex-community@0.0.1`
6
+ > 发布方式 / Publication:npm `latest` + 正式 GitHub Release / full GitHub Release
7
+ > 平台验收 / Platform acceptance:3/3
8
+ > 发布后真实账号验证 / Post-release live validation:0/13
9
+
10
+ ## 中文
11
+
12
+ `dsh-codex-community@0.0.1` 是技术预览版本,使用 npm `latest` 与正式 GitHub Release 发布。发布前必须通过三平台 `3/3`、完整供应链校验和维护者批准;真实账号验证允许以 `0/13` 如实发布,并在发布后持续补齐。
13
+
14
+ ### 新增
15
+
16
+ - `dsh-codex` route、插件专用设置命名空间和 `dsh-codex/openai-codex` OAuth 凭据范围,只接受 ChatGPT OAuth;
17
+ - bundle 不修改通用 `llm-pi-ai` 配置,其他 pi-ai provider 可在同一 profile 使用;
18
+ - pi-ai Codex 模型目录、流式文本/reasoning、Harness 工具调用、usage、图片输入与 replay;
19
+ - DSH Web 中的 Codex 登录页、模型启用管理、最近额度观测卡,以及 `/codex-login status|cancel|logout` 和 `/codex-usage` 命令;
20
+ - `/codex` 当前会话偏好:Fast 开关与 `auto`、SSE、WebSocket 传输选择;偏好只保存在当前进程中;
21
+ - 带 `dsh-codex:` 命名空间的 transport/cache session,以及 reset、agent dispose 和 runtime dispose 的精确资源清理;
22
+ - 现有 `read_image` 对 HTTP(S) 图片 URL 的安全支持,包括公网地址校验、DNS 固定、逐跳重定向校验、总超时、格式与字节上限,以及 2 个活动任务、32 个排队任务的有界调度;
23
+ - 显式的图片请求预算:每张 4,194,304 像素、1,048,576 编码字节,单请求图片总量 20,971,520 字节;
24
+ - `AccountQuotaExceeded` 与五小时账户 usage-window 的结构化识别,映射为非重试 `QUOTA`;
25
+ - 安全半段回复恢复:纯文本会保存,未完成工具调用失败关闭,避免整次请求重放;
26
+ - `transport: auto`、短期上下文缓存,以及 DSH Web 原生图片粘贴/拖放链路;
27
+ - 中英文 Web 设置页的操作互斥、异步卸载安全、长文本换行与 320 像素窄屏布局;
28
+ - 默认中文 README、完整英文镜像、架构、兼容性、排错、测试、发布、贡献与许可规范,以及社区治理文件;
29
+ - 冻结的 DSH 兼容性 smoke 运行时夹具:`test/fixtures/dsh-runtime/pnpm-lock.yaml` 锁定精确 runtime/peer 图,CI、兼容性监测和发布候选统一用 `--frozen-lockfile --ignore-scripts` 安装;
30
+ - Web/profile smoke 成功后生成的 DSH 运行环境证据,绑定夹具 lock SHA-256、Node、pnpm、平台、架构和生命周期脚本禁用状态;
31
+ - 最小权限、Action SHA 固定、一次构建、离线确定性 SBOM、首次发布一次性引导、npm Trusted Publisher 切换和安全重跑的发布流程;审批摘要会展示源提交、包体/Actions 归档双重 SHA-256、验收进度和不可变候选链接,严格门禁在 environment 审批前后各执行一次,npm OIDC 与 GitHub Release 写权限由不同 job 持有。
32
+ - 正式发布策略:发布前硬门禁是 Linux、macOS、Windows `3/3`、完整供应链证据与维护者批准;真实账号进度可以为 `0/13`,所有未验证项必须如实展示,发布后继续验证,问题进入 `0.0.2` 迭代。
33
+ - 不发布、不读取凭据的候选复演命令 `pnpm run release:candidate -- 0.0.1`:要求已提交且工作树干净,并固定 Node 24、pnpm `10.34.5` 与 npm `11.16.0`;它复演 frozen install、完整检查、仅一次生成候选 tarball、dry-run、SBOM、DSH profile smoke、隔离导入与生产依赖 audit,写入 `release/`,已有输出时在写前失败。普通 CI 的独立只读 Ubuntu/Node 24 `candidate-replay` job 动态读取版本后调用该命令一次,并把完整证据上传为保留 14 天的 `dsh-codex-community-${{ github.sha }}-ci-replay` artifact。权威候选仍由 `main` 上的发布 workflow 生成;该复演不替代三平台 CI/profile smoke、真实账号验收或审批。
34
+ - 幂等、离线且保护人工内容的 `release:prepare` 命令,用于生成后续版本双语发布骨架和全新验收记录,并在任何冲突或受管路径符号链接存在时于写入前失败。
35
+ - 不联网、不读取凭据的 `release:acceptance` 离线证据记录器:新增 `pass-platform <linux|macos|windows>`,只记录维护者已核验的 CI/profile smoke,不检查提交是否存在;平台与真实网络证据必须绑定同一候选 SHA,已通过项只允许相同证据的幂等重放,不能覆盖不同证据;真实检查只在明确列齐固定断言后记录通过,状态输出不显示证据值,提交存在性/祖先关系仍由严格门禁验证,审批仍由维护者人工作出。
36
+ - 仅限审批前状态的离线 `pnpm run release:acceptance -- reset-candidate --from-commit=<旧SHA> --to-commit=<新SHA>`:源码变化后清空全部旧平台/live 证据与审批并绑定新候选,再运行新 CI/profile smoke 并用 `pass-platform` 记录;它不检查提交存在性,通过受管路径检查、写锁和原子替换安全写入,拒绝重置 approved 记录,并把 fresh 状态下同一命令的重放保持为 `unchanged`。
37
+
38
+ ### 修复
39
+
40
+ - 阻止 `undefined`、零或非法 `maxPixels` 进入 DSH attachment service;
41
+ - 阻止把账户硬配额耗尽当成普通 429 重试,并把缺少结构化证据的通用 429 窄化为非重试 `QUOTA_OR_RATE_LIMIT`;终止 chunk、直接抛错和 DSH canonical `QUOTA` 统一经过脱敏输出;request metadata 或其他独立 JSON envelope 不再冒充或向命中的 provider `error` envelope 注入 reset/request ID/status,canonical `QUOTA` 仍可从一个自身匹配的 envelope 保留合法事实;
42
+ - 恢复 `STREAM_CLOSED`、WebSocket 中断、直接抛错和无终止 chunk EOF 后的安全纯文本,阻止已产生可见内容或工具调用后盲目重放请求;没有文本的纯工具流也失败关闭,`onRecovery` 观察者异常不会破坏恢复文本、提示与正常终态;
43
+ - 以串行凭据 mutate 回调作为 OAuth 取消线性化点,并用无凭据 generation tracker 在提交阶段拒绝过晚取消,避免误报状态或补偿删除另一 DSH 进程已排队的新登录;退出与 refresh 继续通过同一记录锁串行化,并隔离已结束 attempt 与新登录;真实 Cordis flow owner 卸载会在提交选择前取消,或在提交选择后等待最终写入完成;
44
+ - 不支持类型或结构损坏的已存 OAuth 记录会显示独立的 `invalid` 状态,而不是已登录,且凭据 secret 不跨 RPC;
45
+ - 阻止已取消或插件卸载后的排队图片开始持久化,卸载时中止活动网络阶段并等待已开始的保存收敛,且保存阶段取消后不向调用方返回成功;模型能力预检移入同一限流器,阻塞 resolver 也不能绕过 2 个活动任务与 32 个排队任务的上限;
46
+ - 修复 Web 登录操作竞态、组件卸载后的状态更新、热重载后残留旧样式节点和窄屏横向溢出;
47
+ - 修复 CI 与发布 smoke 直接通过 npm 解算 DSH peer 图可能产生不确定依赖结果和内存失控;现在只安装已提交的冻结 pnpm 运行时图,依赖升级必须同步更新两套 lockfile 并重跑 CI、profile smoke 与受控验收;
48
+ - 修复 Linux 对 PR 模板文件名大小写敏感,以及 Windows CRLF 文本、`pnpm.cmd` 与 npm 打包检查启动方式导致跨平台维护门禁误失败;
49
+ - 修复 Windows DSH profile smoke 结束后的短暂目录锁导致临时清理报 `EBUSY`,以及 `finally` 覆盖真正 smoke 失败;清理现在有限重试,并在双重失败时保留两个错误;
50
+ - 在适配器 profile 与设置保存边界拒绝模型可选字段的显式 `null` 和非有限流空闲超时,避免不可服务值进入请求路径;
51
+ - 修复发布候选过早获得写权限、npm OIDC 与 GitHub Release 写权限共存、provenance 未绑定源仓库/workflow/ref/commit、可变 Action 引用、未 smoke 精确候选、把参考 SBOM 当成实际安装树、缺少生产依赖高危审计、exact SRI 与签名/attestation 校验、GitHub Release 资产未回读、Release/tag 目标在公开前未验证、Release 公开后未完整复核和部分发布后的恢复缺口;直接依赖声明、贡献许可、双语隐私模板和协调依赖升级新增本地门禁,候选证据保留 90 天,只需重跑失败 job。
52
+ - 逐条校验工作流中的每个 `uses:` 是否属于已审核完整提交 allowlist,防止未审核 Action 借用其他位置出现的合法 SHA 绕过门禁。
53
+ - 修复错误分支发布被静默跳过、首次发布无法预先配置 Trusted Publisher、本地 tarball 被 npm 解释为 Git shorthand、签名审计读取错误的 npm 11 attestation 字段,以及旧 Node 20 Action 即将失效;工作流现在按 Registry 状态自动选择受限引导或 OIDC,并对精确候选执行本地路径 dry-run。
54
+
55
+ ### 兼容性
56
+
57
+ - DeepSeek Harness:精确 `0.1.1-rc.2`
58
+ - pi-ai:精确 `0.82.1`
59
+ - Node.js:`>=22.19.0 <25`
60
+ - 本地自动化:macOS arm64 / Node 22.22.2
61
+ - Windows、Linux:Node 22/24 CI、冻结 DSH 安装、完整检查套件与 Web/profile smoke 已通过
62
+ - 真实 OAuth 和真实 Codex 网络 smoke:正式发布时允许 `0/13`,未验证状态如实展示,并在发布后持续补齐
63
+
64
+ ### 已知限制
65
+
66
+ Fast 默认关闭并作为实验能力显式开启;它请求 priority service tier,是否可用取决于账号和服务端,失败后不会自动降级重放。额度卡只显示请求产生的最近观测,不提供实时余额、百分比或主动账户查询;缺少结构化证据的通用 429 只标记为 `QUOTA_OR_RATE_LIMIT`。DSH 当前 `saveImage` 合同不支持取消或回滚,保存开始后的取消不会返回成功,但可能留下由 attachment store 管理的持久化附件。图片生成/编辑不在本版本范围内。`0.0.x` 不承诺稳定 API,请固定安装 `0.0.1`。
67
+
68
+ ### 安全与隐私
69
+
70
+ OAuth grant 由 DSH credentials service 管理。插件不记录 token、完整 provider JSON、提示词或图片。配额诊断只保留 reset 与 request ID。Host 插件与 Harness 共享本机用户权限,不是安全沙箱。
71
+
72
+ ### 发布验收记录
73
+
74
+ 跨平台与真实账号验收记录保存在 [`v0.0.1.acceptance.json`](v0.0.1.acceptance.json)。平台 CI/profile smoke 必须先由维护者核验,再用离线 `pass-platform` 记录;该命令不联网、不检查提交存在性,也不能覆盖不同证据。平台与真实网络证据必须绑定同一 SHA。源码变化后,审批前记录必须先通过 `reset-candidate` 清空全部旧证据与审批、绑定新 SHA,再运行新 CI 并用 `pass-platform` 记录;approved 记录不可重置,相同命令在 fresh 状态下重放为 `unchanged`。真实验收必须逐项证明 Web OAuth、模型目录、文本/reasoning 流、终态 usage、安全工具闭环、两轮 replay、`maxPixels=4194304` 图片路径、四种 transport 与 Fast,不能用自由文本概括替代固定断言。记录只能包含测试时间、环境、布尔结果和证据链接,不能包含 token、OAuth code、Cookie 或账号凭据。严格发布门禁要求平台 `3/3`、完整供应链证据与维护者批准;live 可以保持 `0/13`,正式发布后再从 `pending` 逐项补录为 `passed`。
75
+
76
+ ### 产物验证
77
+
78
+ - `.tgz`:由发布工作流构建一次并作为 Release 附件上传
79
+ - SHA-256:同名 `.sha256` 附件,发布前与 `.tgz` 重新计算比对
80
+ - SRI:同名 `.sri` 附件;发布后必须与 Registry `dist.integrity` 逐字符完全一致
81
+ - CycloneDX SBOM:同名 `.cdx.json` 附件;从已提交的 pnpm lockfile 离线生成并绑定 tarball/lockfile SHA-256,是锁定参考依赖图,不替代实际安装证据
82
+ - 实际安装树:从冻结夹具安装的精确 DSH runtime 与隔离 Host 安装分别保存为 `dsh-runtime-dependency-tree.json`、`isolated-dependency-tree.json`
83
+ - DSH 运行环境:`dsh-runtime-environment.json` 只在 Web/profile smoke 成功后生成,严格门禁会重新校验其中的版本、夹具 lock、Node、pnpm、平台、架构和生命周期脚本状态
84
+ - 生产依赖审计:保存 `npm-audit.json`,high 或 critical 漏洞阻止发布
85
+ - npm 工具链:Trusted Publishing 要求 npm CLI `>=11.5.1`,`--include-attestations` 至少需要 `11.12.0`;本版本固定 `npm@11.16.0`,后续版本建议固定当时最新的兼容版本
86
+ - npm provenance 与签名:保存 Registry attestation bundle,执行 `npm audit signatures --json --include-attestations`,再把已验证 statement 的包摘要、仓库、`release.yml`、`main` 与源提交绑定到本次候选
87
+
88
+ 严格门禁还会校验隔离安装与 Host 导入证据。npm 发布后必须完成 Registry 回读、provenance、签名/attestation 审计和逐字节一致性验证;同版本已存在且字节一致时可安全跳过重复发布并恢复 GitHub Release,在此之前 Release 始终保持非公开状态。候选附件和证据保留 90 天;恢复时只重跑失败 job,继续使用原始候选。
89
+
90
+ ## English
91
+
92
+ `dsh-codex-community@0.0.1` is a technical-preview version published under npm `latest` with a full GitHub Release. Before publication, all three platform gates, complete supply-chain verification, and maintainer approval must pass. Controlled-account validation may be truthfully disclosed at `0/13` and continue after release.
93
+
94
+ ### Added
95
+
96
+ - the `dsh-codex` route, a plugin-scoped settings namespace, and the `dsh-codex/openai-codex` OAuth credential scope, accepting ChatGPT OAuth only;
97
+ - a bundle that leaves the general `llm-pi-ai` configuration unchanged so other pi-ai providers remain usable in the same profile;
98
+ - the pi-ai Codex catalog, streaming text/reasoning, Harness tool calls, usage, image input, and replay;
99
+ - a Codex sign-in page, model enablement management, and recent quota-observation card in DSH Web, plus `/codex-login status|cancel|logout` and `/codex-usage` commands;
100
+ - per-session `/codex` preferences for Fast and `auto`, SSE, or WebSocket transport, kept only in the current process;
101
+ - `dsh-codex:`-namespaced transport/cache sessions with exact resource cleanup on reset, agent disposal, and runtime disposal;
102
+ - safe HTTP(S) image-URL support for the existing `read_image` tool, including public-address checks, DNS pinning, per-hop redirect validation, one total timeout, format and byte limits, and bounded scheduling with two active and 32 queued jobs;
103
+ - explicit image limits: 4,194,304 pixels and 1,048,576 encoded bytes per image, with 20,971,520 image bytes per request;
104
+ - structured recognition of `AccountQuotaExceeded` and the five-hour account usage window, mapped to non-retryable `QUOTA`;
105
+ - safe partial-response recovery: text is preserved and incomplete tool streams fail closed instead of replaying the full request;
106
+ - `transport: auto`, short context retention, and DSH Web's native image paste/drag-and-drop path;
107
+ - mutually exclusive actions, async unmount safety, long-text wrapping, and a 320-pixel narrow-screen layout in the Chinese and English Web settings page;
108
+ - a Chinese-first README, equivalent English README, architecture, compatibility, troubleshooting, testing, release, contribution and licensing rules, and community governance files;
109
+ - a frozen DSH compatibility-smoke runtime fixture: `test/fixtures/dsh-runtime/pnpm-lock.yaml` locks the exact runtime/peer graph, and CI, compatibility monitoring, and release candidates install it consistently with `--frozen-lockfile --ignore-scripts`;
110
+ - DSH runtime-environment evidence generated only after Web/profile smoke succeeds, binding the fixture-lock SHA-256, Node, pnpm, platform, architecture, and disabled-lifecycle-script state;
111
+ - a least-privilege release flow with pinned Action SHAs, one artifact build, an offline deterministic SBOM, a one-time first-publish bootstrap, migration to npm Trusted Publisher, and safe reruns. Its approval summary shows the source commit, package and Actions-archive SHA-256 values, acceptance progress, and immutable-candidate link; the strict gate runs both before and after environment approval, and separate jobs hold npm OIDC and GitHub Release write permission.
112
+ - a formal-publication policy requiring `3/3` Linux, macOS, and Windows gates, complete supply-chain evidence, and maintainer approval before release. Controlled-account progress may remain at `0/13`, every unverified check stays visible, validation continues after publication, and findings are iterated in `0.0.2`.
113
+ - a credential-free, non-publishing candidate replay command, `pnpm run release:candidate -- 0.0.1`. It requires a committed checkout with a clean working tree and pins Node 24, pnpm `10.34.5`, and npm `11.16.0`; it replays frozen installs, the complete checks, exactly one artifact-producing pack, dry-run, SBOM, DSH profile smoke, isolated import, and production-dependency audit into `release/`, failing before any write when output already exists. Regular CI's separate read-only Ubuntu/Node 24 `candidate-replay` job reads the version dynamically, invokes the command once, and uploads the complete evidence as the 14-day `dsh-codex-community-${{ github.sha }}-ci-replay` artifact. The authoritative candidate still comes from the release workflow on `main`; this replay does not replace three-platform CI/profile smoke, live-account acceptance, or approval.
114
+ - an idempotent offline `release:prepare` command that preserves human-authored content, generates future bilingual release skeletons and fresh acceptance records, and fails before writing on conflicts or managed-path symlinks.
115
+ - an offline `release:acceptance` evidence recorder that neither accesses the network nor reads credentials, now with `pass-platform <linux|macos|windows>` to record only maintainer-verified CI/profile smoke without checking that the commit exists. Platform and live-network evidence must bind to one candidate SHA; passed items permit only idempotent replays of identical evidence and cannot be overwritten with different evidence. Live checks pass only when every fixed assertion is explicitly listed, status output hides evidence values, the strict gate still checks commit existence/ancestry, and approval remains a maintainer decision.
116
+ - a pre-approval-only offline `pnpm run release:acceptance -- reset-candidate --from-commit=<old-SHA> --to-commit=<new-SHA>` command that clears all old platform/live evidence and approval fields after source changes, binds the new candidate, and precedes new CI/profile smoke recorded with `pass-platform`. It does not check commit existence, writes safely through managed-path checks, a write lock, and atomic replacement, rejects approved records, and keeps a replay of the same command against fresh state `unchanged`.
117
+
118
+ ### Fixed
119
+
120
+ - Prevent `undefined`, zero, or invalid `maxPixels` from reaching the DSH attachment service.
121
+ - Prevent exhausted account quota from being retried as an ordinary 429, while narrowing generic 429 text without structured evidence to non-retryable `QUOTA_OR_RATE_LIMIT`. Terminal chunks, direct throws, and canonical DSH `QUOTA` now share the same sanitized output. Request metadata or another independent JSON envelope cannot impersonate or inject reset/request ID/status into the matched provider `error` envelope, while canonical `QUOTA` may still retain valid facts from one independently matching envelope.
122
+ - Recover safe partial text after `STREAM_CLOSED`, WebSocket interruption, a direct throw, or EOF without a terminal chunk, and prevent blind full-request replay after visible content or tool calls have started. Tool-only streams with no text also fail closed, and exceptions from the `onRecovery` observer cannot break recovered text, the notice, or the normal terminal event.
123
+ - Linearize OAuth cancellation at the serialized credential mutate callback and reject a too-late cancel during commit through a credential-free generation tracker, preventing both a misleading outcome and a compensating delete of a newer sign-in already queued by another DSH process; continue serializing sign-out and refresh through the same record lock, and isolate retained completed attempts from newer sign-ins. Real Cordis flow-owner disposal now cancels before commit selection or waits for the final write after selection.
124
+ - Present unsupported or malformed stored OAuth records as a distinct `invalid` state instead of signed in, without exposing credential secrets across RPC.
125
+ - Prevent a cancelled or post-disposal queued image from starting persistence, abort active network stages and await already-started saves during disposal, and prevent persistence-stage cancellation from returning success to the caller. Model-capability preflight now runs inside the same limiter, so a blocked resolver cannot bypass the two-active and 32-queued bounds.
126
+ - Fix Web sign-in action races, state updates after unmount, stale style nodes after hot reload, and narrow-screen horizontal overflow.
127
+ - Fix CI and release smoke resolving the DSH peer graph directly through npm, which could produce nondeterministic dependency results and uncontrolled memory use. They now install only the committed frozen pnpm runtime graph; dependency upgrades must update both lockfiles and repeat CI, profile smoke, and controlled acceptance.
128
+ - Fix cross-platform maintenance gates that failed on Linux because of pull-request-template filename casing and on Windows because of CRLF text or `pnpm.cmd` and npm pack-check invocation semantics.
129
+ - Fix transient Windows directory locks after DSH profile smoke causing temporary cleanup to fail with `EBUSY`, and prevent `finally` from masking the real smoke failure. Cleanup now retries within a fixed bound and preserves both errors on a double failure.
130
+ - Reject explicit `null` optional model fields and non-finite stream idle timeouts at the adapter-profile and settings-save boundaries before unserviceable values reach the request path.
131
+ - Fix early write permission for release candidates, co-resident npm OIDC and GitHub Release write permission, provenance not being bound to its source repository/workflow/ref/commit, mutable Action references, a missing exact-candidate smoke, reference SBOMs being treated as actual install trees, missing high-severity production-dependency audit, exact SRI and signature/attestation checks, missing GitHub Release asset readback, missing pre-publication Release/tag target verification, missing full post-publication revalidation, and recovery gaps after partial publication. Local gates now cover direct-dependency notices, contribution licensing, bilingual privacy templates, and coordinated dependency updates. Candidate evidence is retained for 90 days and only failed jobs need to be rerun.
132
+ - Check every workflow `uses:` entry against an allowlist of reviewed full commits, preventing an unreviewed Action from borrowing an approved SHA elsewhere to bypass the gate.
133
+ - Fix wrong-ref release dispatches being silently skipped, the first-publish Trusted Publisher bootstrap deadlock, npm interpreting a local tarball as a Git shorthand, signature audit reading the wrong npm 11 attestation field, and soon-unsupported Node 20 Actions. The workflow now selects the restricted bootstrap or OIDC from Registry state and dry-runs the exact candidate as a local path.
134
+
135
+ ### Compatibility
136
+
137
+ - DeepSeek Harness: exact `0.1.1-rc.2`
138
+ - pi-ai: exact `0.82.1`
139
+ - Node.js: `>=22.19.0 <25`
140
+ - Local automation: macOS arm64 / Node 22.22.2
141
+ - Windows and Linux: Node 22/24 CI, frozen DSH installation, the full check suite, and Web/profile smoke pass
142
+ - Real OAuth and real Codex network smoke may remain at `0/13` for formal publication; unverified status stays visible and validation continues afterward
143
+
144
+ ### Known limitations
145
+
146
+ Fast is off by default and enabled explicitly as an experimental capability. It requests the priority service tier, whose availability depends on the account and service; failures are not replayed automatically on a lower tier. The quota card shows only recent request-derived observations, not a live balance, percentage, or proactive account query; generic 429 text without structured evidence is labeled only `QUOTA_OR_RATE_LIMIT`. The current DSH `saveImage` contract has no cancellation or rollback support, so cancellation after persistence begins does not return success but may leave a durable attachment managed by the attachment store. Image generation and editing are outside this release. The `0.0.x` line does not promise a stable API; pin `0.0.1`.
147
+
148
+ ### Security and privacy
149
+
150
+ The DSH credentials service owns OAuth grants. The plugin does not log tokens, complete provider JSON, prompts, or images. Quota diagnostics retain only reset and request ID. A Host plugin shares local-user privileges with Harness and is not a security sandbox.
151
+
152
+ ### Acceptance record
153
+
154
+ Cross-platform and controlled-account acceptance are recorded in [`v0.0.1.acceptance.json`](v0.0.1.acceptance.json). A maintainer must verify platform CI/profile smoke before recording it with the offline `pass-platform` command, which does not access the network, check commit existence, or overwrite different evidence. Platform and live-network evidence must bind to one SHA. After source changes, a pre-approval record must first use `reset-candidate` to clear all old evidence and approval fields, bind the new SHA, rerun CI, and record it with `pass-platform`; an approved record cannot be reset, while the same command replayed against fresh state is `unchanged`. Live acceptance must separately prove Web OAuth, the model catalog, text/reasoning streams, terminal usage, a safe tool round trip, two-turn replay, the `maxPixels=4194304` image path, all four transports, and Fast; free-form scope text cannot replace the fixed assertions. The record may contain only timestamps, environments, boolean results, and evidence links, never tokens, OAuth codes, cookies, or account credentials. The strict publication gate requires `3/3` platforms, complete supply-chain evidence, and maintainer approval; live may remain at `0/13` and move one check at a time from `pending` to `passed` after formal publication.
155
+
156
+ ### Artifact verification
157
+
158
+ - `.tgz`: built exactly once by the release workflow and uploaded as a Release asset
159
+ - SHA-256: the matching `.sha256` asset, recomputed against the `.tgz` before publication
160
+ - SRI: the matching `.sri` asset; after publication it must exactly equal the Registry `dist.integrity` value character for character
161
+ - CycloneDX SBOM: the matching `.cdx.json` asset, generated offline from the committed pnpm lockfile and bound to tarball/lockfile SHA-256; it is a locked reference dependency graph, not a replacement for actual-install evidence
162
+ - Actual install trees: the exact DSH runtime installed from the frozen fixture and the isolated Host installation are recorded separately as `dsh-runtime-dependency-tree.json` and `isolated-dependency-tree.json`
163
+ - DSH runtime environment: `dsh-runtime-environment.json` is generated only after Web/profile smoke succeeds, and the strict gate re-verifies its versions, fixture lock, Node, pnpm, platform, architecture, and lifecycle-script state
164
+ - Production-dependency audit: `npm-audit.json` is preserved, and any high or critical vulnerability blocks publication
165
+ - npm toolchain: Trusted Publishing requires npm CLI `>=11.5.1`, and `--include-attestations` requires at least `11.12.0`; this release pins `npm@11.16.0`, and future releases should pin the latest compatible version available at the time
166
+ - npm provenance and signatures: preserve the Registry attestation bundle, run `npm audit signatures --json --include-attestations`, and bind the verified statement's package digest, repository, `release.yml`, `main`, and source commit to the candidate
167
+
168
+ The strict gate also validates isolated installation and Host-import evidence. Registry readback, provenance, signature/attestation audit, and byte-for-byte comparison are mandatory after npm publication. A byte-identical existing version safely skips duplicate publication and resumes GitHub Release recovery; the Release remains non-public until those checks pass. Candidate assets and evidence are retained for 90 days; recovery reruns only failed jobs and continues from the original candidate.
169
+
170
+ ## 商标与非隶属 / Trademarks and non-affiliation
171
+
172
+ 本项目由社区维护,与 OpenAI 或 DeepSeek 无隶属或背书关系。OpenAI、ChatGPT、Codex、DeepSeek 及相关标识归各自权利人所有。
173
+
174
+ This project is community-maintained and is not affiliated with or endorsed by OpenAI or DeepSeek. OpenAI, ChatGPT, Codex, DeepSeek, and related marks belong to their respective owners.
@@ -0,0 +1,290 @@
1
+ # Release process
2
+
3
+ [简体中文](releasing.md) | [English](releasing.en.md)
4
+
5
+ Versioning begins at `0.0.1`. The `0.0.x` line is a technical preview, but `0.0.1` is still published under npm `latest` with a full GitHub Release. Every Release requires complete Chinese and English copy. The pre-publication hard gates are `3/3` platforms, complete supply-chain evidence, and maintainer approval; controlled-account validation may start at `0/13`, continue after formal publication, and must remain honestly disclosed.
6
+
7
+ ## Preparation
8
+
9
+ 1. Create a clearly named release branch from `main`;
10
+ 2. update `package.json`, `CHANGELOG.md`, the README feature and support-boundary statements, compatibility date, and `docs/releases/v<version>.md`;
11
+ 3. confirm package name, repository URL, license, peer range, and `dsh.bundle`;
12
+ 4. review and update both the root `pnpm-lock.yaml` and `test/fixtures/dsh-runtime/pnpm-lock.yaml`; never update only one dependency graph;
13
+ 5. require green Linux, macOS, and Windows CI before merge;
14
+ 6. state the controlled-account validation plan and current progress. `0.0.1` may be formally published at `0/13`, then completed check by check with a controlled test account without recording tokens, OAuth codes, cookies, or account credentials.
15
+
16
+ Use the offline preparation script to establish the next release skeleton:
17
+
18
+ ```sh
19
+ pnpm run release:prepare -- 0.0.2
20
+ ```
21
+
22
+ It only creates or completes the `package.json` version, bilingual `CHANGELOG.md` draft, bilingual Release draft, and a fresh schema v3 draft acceptance record. The script is idempotent, accesses neither Git nor the network, and never fabricates dates, commits, or passing evidence. Existing valid human-authored content is preserved, and any conflict fails before writing. The README, compatibility documents, lockfiles, release branch, commit, and tag still require manual handling.
23
+
24
+ ### Replaying a candidate locally or in CI
25
+
26
+ From a committed candidate checkout with a clean working tree, use Node 24, pnpm `10.34.5`, and npm `11.16.0` to replay the `0.0.1` candidate in one command:
27
+
28
+ ```sh
29
+ pnpm run release:candidate -- 0.0.1
30
+ ```
31
+
32
+ The command runs on a local development machine or in separate CI. It neither publishes an npm package or GitHub Release nor reads npm tokens, OIDC, OAuth, or other credentials. It performs the root and frozen-DSH-fixture frozen installs, the complete `check`, exactly one artifact-producing `npm pack`, a local-tarball publish dry-run, the deterministic SBOM, exact-candidate DSH profile smoke, isolated installation and Host import, and the production-dependency audit. It writes the candidate package, digests, and all evidence under `release/`. To prevent stale files from being mistaken for current evidence, any existing candidate output makes the command fail before its first write; remove or move only old output that you have confirmed is no longer needed before retrying.
33
+
34
+ Regular CI's separate read-only `candidate-replay` job uses a clean Ubuntu/Node 24 checkout, pinned pnpm `10.34.5`, and npm `11.16.0`. It reads the version dynamically from `package.json`, invokes the unified command exactly once, and uploads the complete `release/` directory as the `dsh-codex-community-${{ github.sha }}-ci-replay` artifact with 14-day retention.
35
+
36
+ This is only a reproducible local/CI preflight. The authoritative release candidate must still be generated and uploaded by the `release.yml` workflow on `main`. A local replay on macOS or Windows cannot replace that workflow's Linux x64 publication evidence, three-platform CI/profile smoke, the supply-chain gate, or maintainer approval. It also does not perform post-release controlled-account validation.
37
+
38
+ ## Two gate levels
39
+
40
+ Regular CI uses draft mode. It permits `TBD` and `pending`, while checking the version, bilingual Release structure, and acceptance-record shape so unreleased work remains continuously testable:
41
+
42
+ ```sh
43
+ pnpm install --frozen-lockfile
44
+ pnpm run check
45
+ pnpm run verify:release
46
+ ```
47
+
48
+ `npm publish` uses strict mode. `0.0.1` may retain controlled-account validation at `0/13`, so `pending` and its corresponding `TBD` fields are valid, truthful states inside live records. Publication fails before any network write if any of these publication conditions is unmet:
49
+
50
+ - the Release date, Accepted commit, current-version CHANGELOG date, or another publication-critical field is still a placeholder;
51
+ - `docs/releases/v<version>.acceptance.json` does not record maintainer approval for formal publication;
52
+ - Linux, macOS, and Windows CI/profile smoke have not all passed with `3/3` evidence bound to the same candidate commit;
53
+ - platform timestamps, runner details, Node versions, approver, or HTTPS evidence links are missing;
54
+ - files outside the permitted release-evidence set changed after the accepted commit; only the bilingual Release body, acceptance JSON, bilingual READMEs, `CHANGELOG.md`, and bilingual compatibility documents may be updated to clear publication state;
55
+ - the `.tgz`, SHA-256, SRI, locked reference SBOM, actual install trees, production-dependency audit, or isolated-import evidence is missing or inconsistent.
56
+
57
+ Both the release workflow and `prepublishOnly` invoke the strict command. With `publish=true`, the candidate job first binds the candidate package, isolated-import evidence, and current release commit to strict verification; the protected environment is not requested until that verification passes. After approval, the publication job downloads the same immutable candidate and repeats the identical verification. To reproduce that invocation from the repository root, use:
58
+
59
+ ```sh
60
+ PACKAGE_VERSION="$(node -p "require('./package.json').version")"
61
+ RELEASE_PACKAGE_FILE="release/dsh-codex-community-${PACKAGE_VERSION}.tgz" \
62
+ RELEASE_ISOLATED_IMPORT_EVIDENCE=release/isolated-import.json \
63
+ RELEASE_SOURCE_COMMIT="$(git rev-parse HEAD)" \
64
+ pnpm run verify:release:publish
65
+ # After Registry publication and provenance/signature readback, upload immutable Registry evidence; the GitHub Release job has no OIDC and downloads and rechecks that evidence before writing the Release
66
+ ```
67
+
68
+ Do not bypass the gate with fabricated environment variables. Keep the release in draft state until all three platform, supply-chain, and maintainer-approval records exist. Missing live evidence remains `pending` and must never be presented as verified.
69
+ `prepublishOnly` protects publication from a repository checkout. npm does not run that lifecycle when publishing an existing tarball, so the workflow runs the strict gate once before approval and again immediately before publishing that same `.tgz`; it requests a short-lived OIDC identity token only after the protected environment approves the job.
70
+
71
+ ## Acceptance record
72
+
73
+ 1. Run cross-platform CI/profile smoke and complete supply-chain verification against a committed release candidate.
74
+ 2. Let the first `pass-platform` bind that candidate's full 40-character commit to `testedCommit`.
75
+ 3. Run the profile smoke on all three operating systems with exact DSH `0.1.1-rc.2` and a tested Node 22 release (at least `22.19.0`) or Node 24. After a maintainer verifies the evidence, use `pass-platform` to record `testedAt`, environment details, and HTTPS evidence links without sensitive parameters.
76
+ 4. After all three platforms, the supply-chain evidence, and the candidate boundary pass, a maintainer records approval and changes `releaseStatus` to `approved`. This approves formal publication; it does not claim that controlled-account validation is `13/13`.
77
+ 5. `0.0.1` may retain live validation at `0/13` and be published under npm `latest` with a full GitHub Release. The Release and compatibility documents must disclose every unverified check honestly.
78
+ 6. After publication, manually complete the live checks below with a test account in a controlled local environment. This consumes account quota. Mark a check `passed` only after every fixed assertion has sanitized evidence; a free-form scope statement is not a substitute.
79
+ 7. Before publication, any source, configuration, dependency, lockfile, or workflow change after the candidate was bound requires resetting the draft record to the new commit and repeating CI, profile smoke, and supply-chain verification; an approved record cannot be reset. Product findings after formal publication are fixed in `0.0.2`, never by overwriting `0.0.1`.
80
+
81
+ | Check | Required proof | Copyable exact `--assert` arguments |
82
+ | --- | --- | --- |
83
+ | `oauthWebSignIn` | OAuth starts from DSH Settings, the credential becomes configured, and a post-sign-in request succeeds | `--assert=flowStartedFromSettings --assert=credentialConfigured --assert=postSignInRequestSucceeded` |
84
+ | `modelCatalog` | Settings shows the catalog and the conversation model selector can select a Codex model | `--assert=settingsCatalogVisible --assert=conversationModelSelectable` |
85
+ | `textStream` | A non-empty text delta and a normal terminal event are observed | `--assert=nonEmptyTextDelta --assert=terminalStopObserved` |
86
+ | `reasoningStream` | A reasoning block is observed and the request terminates normally | `--assert=reasoningBlockObserved --assert=terminalStopObserved` |
87
+ | `terminalUsage` | Terminal usage contains both input and output token counts | `--assert=inputTokensObserved --assert=outputTokensObserved` |
88
+ | `toolRoundTrip` | A safe test tool is requested, executed, returned, and followed by a successful response | `--assert=toolCallObserved --assert=toolExecuted --assert=toolResultReturned --assert=followUpSucceeded` |
89
+ | `replayContinuity` | Two turns succeed and the second correctly uses first-turn context | `--assert=firstTurnSucceeded --assert=secondTurnUsedPriorContext --assert=secondTurnSucceeded` |
90
+ | `imageMaxPixels` | Native image input succeeds and the request projects `maxPixels=4194304` | `--assert=nativeImageAccepted --assert=maxPixels4194304Projected --assert=requestSucceeded` |
91
+ | `transportAuto` | At least one live request succeeds through the auto transport | `--assert=requestSucceeded` |
92
+ | `transportSse` | At least one live request succeeds through the SSE transport | `--assert=requestSucceeded` |
93
+ | `transportWebsocket` | At least one live request succeeds through the WebSocket transport | `--assert=requestSucceeded` |
94
+ | `transportWebsocketCached` | Two consecutive turns succeed in the same session | `--assert=firstTurnSucceeded --assert=secondTurnSucceeded` |
95
+ | `fastPriority` | Priority is requested, the live request succeeds, and no automatic downgrade replay occurs | `--assert=priorityRequested --assert=requestSucceeded --assert=noAutomaticDowngrade` |
96
+
97
+ Each check contains only `status`, `testedAt`, an HTTPS `evidenceUrl` without query data, and the gate-defined boolean `assertions`. Evidence must not contain a token, OAuth code, cookie, account identifier, or complete private conversation.
98
+
99
+ macOS/Linux:
100
+
101
+ ```sh
102
+ DSH_BIN=/path/to/dsh pnpm run smoke:dsh-profile
103
+ ```
104
+
105
+ Windows PowerShell:
106
+
107
+ ```powershell
108
+ $env:DSH_BIN = "C:\path\to\dsh.exe"
109
+ pnpm run smoke:dsh-profile
110
+ ```
111
+
112
+ ### Cross-platform CI/profile-smoke recording
113
+
114
+ After a maintainer verifies the CI and profile-smoke evidence for a platform, record the result with this offline command:
115
+
116
+ ```sh
117
+ pnpm run release:acceptance -- pass-platform <linux|macos|windows> \
118
+ --tested-commit=<full-40-character-lowercase-SHA> \
119
+ --tested-at=<RFC3339-with-timezone> \
120
+ --runner=<single-line-runner-id> \
121
+ --node-version=<22.x.y-or-24.x.y> \
122
+ --dsh-version=0.1.1-rc.2 \
123
+ --profile-smoke=passed \
124
+ --evidence-url=<sanitized-HTTPS>
125
+ ```
126
+
127
+ All seven options are required exactly once and must use the `--name=value` form. `--runner` must be a trimmed, non-placeholder, single-line identifier of at most 128 characters with no control characters. `--node-version` accepts a stable three-part Node 22 release (at least `22.19.0`) or Node 24, optionally prefixed with `v`. `--dsh-version` and `--profile-smoke` must be exactly `0.1.1-rc.2` and `passed`, respectively. `--evidence-url` must be an absolute HTTPS URL without userinfo, query data, or a fragment.
128
+
129
+ `pass-platform` only writes a CI/profile-smoke result that a maintainer has already verified to the acceptance record; it does not run the smoke, access the network, or check that the commit exists. The first `pass-platform` or `pass` binds the full candidate SHA, and all later platform and live-network evidence must use that same SHA. A passed item permits only a semantically identical idempotent replay; any differing field conflicts and cannot overwrite existing evidence. The same identical-replay-only rule applies after approval.
130
+
131
+ ### Post-release local controlled-account validation
132
+
133
+ A maintainer must perform real-account operations manually in a controlled local environment. For `0.0.1`, these checks run after formal publication and incomplete items remain `pending`. `release:acceptance` is only an offline evidence recorder: it does not access the network, read credentials, make provider requests, infer assertions, or replace human judgment.
134
+
135
+ Inspect the current state first:
136
+
137
+ ```sh
138
+ pnpm run release:acceptance -- status
139
+ ```
140
+
141
+ `status` displays only passed/pending counts and check names; it never displays evidence values. After manually completing one check and reviewing its sanitized evidence, repeat `--assert` to explicitly list every fixed assertion required by that check:
142
+
143
+ ```sh
144
+ pnpm run release:acceptance -- pass <check> \
145
+ --tested-commit=<full-40-character-lowercase-SHA> \
146
+ --tested-at=<RFC3339> \
147
+ --evidence-url=<sanitized-HTTPS> \
148
+ --assert=<fixed-assertion> \
149
+ --assert=<fixed-assertion>
150
+ ```
151
+
152
+ The first `pass-platform` binds the candidate while the record's `testedCommit` is still `TBD`. Every later `pass-platform`, `pass`, and `approve` must provide the same full 40-character lowercase SHA, preventing platform and live-network evidence from different candidates from being mixed. `pass` records `passed` only when the assertion names are complete and exactly match the fixed table above; it never infers, fills, or passes assertions automatically. Live validation may continue from `pending` to `passed` after a maintainer approves and publishes `0.0.1`. Record publication approval once all three platform and supply-chain records are complete:
153
+
154
+ ```sh
155
+ pnpm run release:acceptance -- approve \
156
+ --tested-commit=<full-40-character-lowercase-SHA> \
157
+ --approved-by=<public-maintainer-id> \
158
+ --approved-at=<RFC3339> \
159
+ --evidence-url=<sanitized-HTTPS>
160
+ ```
161
+
162
+ The command checks only the SHA format and its consistency with the acceptance record, and records a human approval that has already been made; it does not approve on a maintainer's behalf. The strict publish gate still verifies that the commit exists and that the candidate is an ancestor of the release commit. Tokens, OAuth codes, cookies, account identifiers, and complete private content must never appear in arguments, evidence URLs, acceptance records, or terminal logs.
163
+
164
+ ### Resetting the candidate commit
165
+
166
+ When source or other accepted content changes after a draft candidate has been bound, replace the old candidate with the new commit first:
167
+
168
+ ```sh
169
+ pnpm run release:acceptance -- reset-candidate \
170
+ --from-commit=<old-full-40-character-lowercase-SHA> \
171
+ --to-commit=<new-full-40-character-lowercase-SHA>
172
+ ```
173
+
174
+ Both SHAs must be full, lowercase, and different; a normal reset requires the current `testedCommit` to match `--from-commit`. `reset-candidate` accepts only a draft record. It clears every old platform result, live-network result, and approval field, restores all platform/live state to `pending`/`TBD`/`false`, and binds `testedCommit` to `--to-commit`. An approved record cannot be reset.
175
+
176
+ The command runs offline, accesses neither the network nor Git, and does not check whether either commit exists. Managed-path boundary checks, a concurrent-write lock, and atomic replacement protect the acceptance file. Replaying the same `old → new` command returns `unchanged` when the record is already a fresh draft with only the new SHA bound; any residual platform, live, or approval evidence instead conflicts and produces no write. After the reset, run cross-platform CI/profile smoke against the new SHA first, then record the new evidence with `pass-platform`; every later platform, live, and approval command must continue to use that new SHA.
177
+
178
+ Before publication, fill in the Release date and accepted commit from `testedCommit`, then remove every placeholder. The workflow records the exact release commit used to build the artifact in the isolated-import evidence and uploads it as an asset.
179
+
180
+ ## Artifact gate
181
+
182
+ The release workflow runs these stages in order:
183
+
184
+ ```sh
185
+ PACKAGE_VERSION="$(node -p "require('./package.json').version")"
186
+ PACKAGE_FILE="release/dsh-codex-community-${PACKAGE_VERSION}.tgz"
187
+ pnpm install --frozen-lockfile --ignore-scripts
188
+ pnpm --dir test/fixtures/dsh-runtime install --frozen-lockfile --ignore-scripts
189
+ pnpm run check
190
+ pnpm run verify:release
191
+ mkdir -p release
192
+ npm install --global --ignore-scripts --no-audit --no-fund npm@11.16.0
193
+ npm --version > release/npm-cli-version.txt
194
+ npm pack --ignore-scripts --json --pack-destination release > release/pack.json
195
+ # Dry-run the local tarball through ./$PACKAGE_FILE, then generate SHA-256, SRI, and the locked reference SBOM
196
+ npm publish "./$PACKAGE_FILE" --dry-run --force --ignore-scripts --json
197
+ DSH_CLI_ROOT="$PWD/test/fixtures/dsh-runtime" DSH_PLUGIN_PACKAGE="$PACKAGE_FILE" pnpm run smoke:dsh-profile
198
+ # Record the DSH environment, actual install trees, production-dependency audit, and isolated-import evidence, then upload the immutable candidate and write an approval summary with both SHA-256 values and its download link
199
+ # With publish=true, run this strict gate in the candidate job first; only then request environment approval, after which the publish job downloads the same candidate and repeats it:
200
+ npm install --global --ignore-scripts --no-audit --no-fund npm@11.16.0
201
+ pnpm install --frozen-lockfile --ignore-scripts
202
+ RELEASE_PACKAGE_FILE="$PACKAGE_FILE" \
203
+ RELEASE_ISOLATED_IMPORT_EVIDENCE=release/isolated-import.json \
204
+ RELEASE_SOURCE_COMMIT="$(git rev-parse HEAD)" \
205
+ pnpm run verify:release:publish
206
+ ```
207
+
208
+ Build the `.tgz` exactly once, then generate SHA-256, SRI, and a CycloneDX SBOM. The SRI must exactly equal the npm Registry `dist.integrity` readback; comparing only an algorithm prefix or a reformatted value is insufficient.
209
+
210
+ The CycloneDX SBOM is a **locked reference dependency graph** generated offline from the committed `pnpm-lock.yaml` and package manifest. It proves what the candidate source declares and locks; it does not claim to be the complete tree produced by a particular installation. The generator runs only `pnpm list --prod --json --depth Infinity --lockfile-only`; it never runs `npm install`, `npm sbom`, or re-resolves dependencies from the Registry. The output is deduplicated and stably sorted, omits local absolute paths and download URLs, binds the tarball SHA-256, lockfile SHA-256, and package dependency-descriptor SHA-256, and verifies that the artifact's package name, version, license, repository, and production/peer dependency descriptors match the repository manifest.
211
+
212
+ The root `pnpm-lock.yaml` locks the plugin's build and release dependencies. `test/fixtures/dsh-runtime/pnpm-lock.yaml` separately locks the complete DSH runtime and peer graph used by compatibility smoke. CI, compatibility monitoring, and release candidates all run `pnpm --dir test/fixtures/dsh-runtime install --frozen-lockfile --ignore-scripts`; they never resolve an unbounded peer graph at runtime through `npm install @deepseek-ai/dsh@...`. Direct npm resolution can consume uncontrolled memory and can produce a different runtime for identical source as Registry state changes.
213
+
214
+ That same artifact is then installed into an isolated profile by the fixture's exact `0.1.1-rc.2` DSH runtime for a Web startup smoke, and it is also installed with `--ignore-scripts` in an empty directory and imported as a Host plugin. The two actual installations are recorded separately as `dsh-runtime-dependency-tree.json` and `isolated-dependency-tree.json`, alongside the reference SBOM. Only after the Web/profile smoke passes does the workflow generate `dsh-runtime-environment.json`, recording the DSH version, fixture-lock SHA-256, complete Node version, pnpm version, platform, architecture, and disabled-lifecycle-script state; the strict gate recomputes and verifies those values from the release commit. The isolated install also runs `npm audit --omit=dev --audit-level=high --json` and saves `npm-audit.json`; any high or critical production-dependency vulnerability blocks publication. The candidate job has only `contents: read`, and its artifact plus evidence are retained for 90 days. Its approval summary shows the version, source commit, package SHA-256, Actions archive SHA-256, acceptance progress, and candidate download link. Only `publish=true`, `refs/heads/main`, and a passing pre-approval strict gate can request approval from the protected `npm-release` environment. The Registry job has only `contents: read` and `id-token: write`; after npm publication, byte-for-byte readback, signature checks, and provenance binding to the package digest, repository, `release.yml`, `main`, and source commit, it uploads immutable Registry evidence. The following GitHub Release job has only `contents: write` and no OIDC; it downloads and rechecks that evidence before writing the Release. After the Release becomes public, the workflow rechecks its bilingual body, title, tag, exact asset set, and every asset byte before confirming the tag commit.
215
+
216
+ Because the frozen fixture uses `--ignore-scripts`, this layer proves DSH Web/profile compatibility with this plugin; it does not claim to validate native terminal or native-build capabilities in DSH dependencies that require lifecycle scripts.
217
+
218
+ An upgrade to DSH, pi-ai, or another runtime dependency must update and review both lockfiles, confirm that the fixture still pins only the intended DSH version, repeat complete cross-platform CI, profile smoke, and supply-chain verification before publication, and restart controlled live validation afterward. A scheduled Registry drift report does not replace this upgrade process.
219
+
220
+ ## Release environment
221
+
222
+ Create `npm-release` under **GitHub repository Settings → Environments**:
223
+
224
+ 1. configure at least one required reviewer;
225
+ 2. restrict deployment branches to `main`;
226
+ 3. do not add a normal repository-level npm token; only during the first-publish bootstrap, temporarily add the environment secret `NPM_BOOTSTRAP_TOKEN`;
227
+ 4. have the releaser manually dispatch the workflow; approve the environment only after the candidate job's pre-approval strict gate passes and its summary confirms the version, source commit, both SHA-256 values, acceptance progress, and immutable candidate download.
228
+
229
+ The release workflow pins npm `11.16.0` and verifies and records that exact version before any network write. Trusted Publishing requires npm CLI `11.5.1` or newer, while `--include-attestations` requires at least `11.12.0`; an npm upgrade must update the pin, tests, and release documentation together.
230
+
231
+ Run the read-only candidate first:
232
+
233
+ ```sh
234
+ gh workflow run release.yml --ref main \
235
+ -f version=0.0.1 \
236
+ -f publish=false
237
+ ```
238
+
239
+ `--ref main` is a hard gate. Dispatching another branch makes the candidate fail explicitly instead of reporting success with every job skipped. Download and review the candidate assets, confirm `3/3` platforms, complete supply-chain evidence, and maintainer approval, then update only the permitted release-evidence documents and merge them to `main` before publishing. Live progress at `0/13` does not block the formal `0.0.1` release, but it must remain honestly disclosed.
240
+
241
+ ## First publication of `0.0.1`
242
+
243
+ npm allows Trusted Publisher configuration only for a package that **already exists**, so a new package needs a one-time bootstrap. This exception is valid only for `dsh-codex-community@0.0.1` while the package name is absent from the Registry. The workflow checks the version, package-name state, and exact candidate and fails when any condition differs.
244
+
245
+ 1. In the npm website, create a shortest-lived Granular Access Token. Set Packages and scopes to **Read and write**, select **All packages**, and enable **Bypass 2FA**. The token cannot be restricted to a package that does not exist yet, so revoke it immediately after success;
246
+ 2. paste the token only into the GitHub `npm-release` environment secret `NPM_BOOTSTRAP_TOKEN`. Never put it in the repository, shell history, an Issue, Release evidence, or chat;
247
+ 3. after the platform, supply-chain, and maintainer-approval gates pass, dispatch the one-time formal publication; the workflow uses npm `latest` and creates a full GitHub Release:
248
+
249
+ ```sh
250
+ gh workflow run release.yml --ref main \
251
+ -f version=0.0.1 \
252
+ -f publish=true
253
+ ```
254
+
255
+ 4. approve the `npm-release` environment. The workflow injects this secret into `npm publish` only when it must create the absent `0.0.1`, and generates provenance on a GitHub-hosted runner;
256
+ 5. once the package exists, open its **Settings → Trusted Publisher** and enter: provider `GitHub Actions`, owner `yoshino-xiao7`, repository `dsh-codex`, workflow filename `release.yml`, environment `npm-release`, and allowed action `npm publish`;
257
+ 6. revoke the npm token and delete the GitHub environment secret:
258
+
259
+ ```sh
260
+ gh secret delete NPM_BOOTSTRAP_TOKEN --env npm-release
261
+ ```
262
+
263
+ 7. under **Settings → Publishing access**, select **Require two-factor authentication and disallow tokens**, then verify the Trusted Publisher configuration;
264
+ 8. the workflow automatically selects Trusted Publisher for every later version. While the package name is absent from the Registry, only `0.0.1` may enter the one-time bootstrap; once the package exists, the workflow does not read the bootstrap secret. If `0.0.1` already exists and is byte-identical to the candidate, a rerun only recovers later Release steps and no longer needs the token.
265
+
266
+ This bootstrap follows npm's [Trusted Publisher limitation](https://docs.npmjs.com/trusted-publishers/), [2FA and Granular Access Token requirements](https://docs.npmjs.com/requiring-2fa-for-package-publishing-and-settings-modification/), and [GitHub Actions provenance requirements](https://docs.npmjs.com/generating-provenance-statements/).
267
+
268
+ ## Routine publication
269
+
270
+ After Trusted Publisher is configured, every version uses:
271
+
272
+ ```sh
273
+ gh workflow run release.yml --ref main \
274
+ -f version=0.0.2 \
275
+ -f publish=true
276
+ ```
277
+
278
+ - `publish=false` only builds and verifies a candidate artifact; `publish=true` enables the strict gate, Registry write, and bilingual GitHub Release.
279
+ - `0.0.1` publishes under npm `latest` with a full GitHub Release. Controlled-account progress may be `0/13`, but the documentation must disclose that status exactly.
280
+ - Routine publication uses GitHub OIDC only and reads no token.
281
+ - After npm publication, read back `repository.url`, `dist.integrity`, and provenance, requiring `dist.integrity` to exactly match the candidate `.sri` value.
282
+ - Install the exact Registry version in a fresh directory and run `npm audit signatures --json --include-attestations`. A signature or attestation verification failure blocks the public Release; preserve the raw provenance bundle and audit result as Release assets.
283
+ - Download the registry tarball and compare it byte-for-byte with the workflow artifact.
284
+ - If npm already contains the version, download it first: skip `npm publish` and continue Release recovery only when it is byte-identical, otherwise fail immediately.
285
+ - Keep the GitHub Release as a draft while uploading or restoring every asset with `--clobber`. Download every Release asset and compare it byte-for-byte before making the Release public. A rerun of an already-public version succeeds only when the tag commit and every asset still match the candidate.
286
+ - Do not tag or publish directly from a development machine.
287
+
288
+ Continue collecting controlled-account evidence against the formal release. If validation finds a product issue, fix it and publish `0.0.2`; never modify, overwrite, or republish the existing `0.0.1`.
289
+
290
+ Any failed gate stops the release, and an immutable npm version is never overwritten. A byte-identical Registry artifact permits safe recovery of later stages; a different artifact requires a fixed, incremented version. If the candidate job succeeded but the publication job failed, rerun only the failed job in the same workflow run, leaving the successful candidate job untouched. Recovery keeps the original `GITHUB_SHA` and the uploaded candidate retained for 90 days even if `main` has advanced, while a new workflow run rebuilds the then-current `main`.