@jayyuen66/dsh-quality-gate 0.1.5 → 0.1.6
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 +47 -24
- package/README.md +7 -7
- package/README.zh-CN.md +7 -7
- package/client.js +20 -365
- package/cordis.patch.yml +3 -0
- package/host.js +47 -44
- package/package.json +48 -45
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
格式遵循 [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),版本号遵循
|
|
4
|
-
[Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
4
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html)。最新版本在最上。这里的偏离是刻意的:
|
|
5
|
+
版本标题不写发布日期(各版发布时间以 npm registry 为准,`npm view <包名> time` 取得到),并按需
|
|
6
|
+
增设标准六类之外的小节(如 `Tests`、`Not changed, deliberately`)。
|
|
7
|
+
|
|
8
|
+
## 0.1.6
|
|
9
|
+
|
|
10
|
+
`apply_patch` 进入编辑台账与回合累加器的记账面;宿主开发基线抬到 `0.2.2-rc.1`,支持窗口从单条 `^0.2.1-rc.3` 改成两条宿主线的并集,`engines.node` 地板抬到 `>=24.9.0`;卡片组件改由 shared 的 `./client` 供。默认配置与检查白名单不变,需要留意的是 `engines.node` 抬高后 node 22 装不上本包。
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`apply_patch` 记账**:编辑台账与回合累加器按补丁的每个操作目标记账(含 move 的源与目的);失败结果否定同 callId 的全部行。补丁解析复用 shared 的官方 `dsh-fs-patch` 封装。
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **shared 抬到 0.2.4**:catalog、生成物与 lockfile 随之同步;shared 新增的普通依赖 `@deepseek-ai/dsh-fs-patch` 由 shared 自己安装,本包不重复声明。
|
|
19
|
+
- **宿主开发基线**:`devDependencies` 的 dsh 家族抬到 `0.2.2-rc.1`,`@deepseek-ai/cordis` 抬到 `4.1.0`、cordis 插件组两枚抬到 `1.1.0`、`@deepseek-ai/schemastery` 抬到 `3.19.0`;根 catalog 与 overrides 的投影同步,`pnpm-lock.yaml` 随之重解。
|
|
20
|
+
- **支持窗口写成并集**:`engines.dsh` 与 `@deepseek-ai/dsh`、`@deepseek-ai/dsh-credentials` 两枚 peer 由 `^0.2.1-rc.3` 改为 `^0.2.1-rc.5 || ^0.2.2-alpha.3`;`@deepseek-ai/schemastery` 的 peer 由 `^3.18.6-alpha.1` 放宽为 `^3.18.5 || ^3.18.6-alpha.1`,与两条宿主线各自的实装版本对齐。caret 的预发布比较只吃自己那条元组,单线会把另一条线上的预发布判成不满足,而装载门(`app-boot` 带 `includePrerelease` 读 `engines.dsh`)没有这个差别——把窗口写窄只会让发布契约与安装器各说一套。`engines.node` 由 `>=22.12.0` 抬到 `>=24.9.0`,取值取自宿主自己的地板。
|
|
21
|
+
- **卡片组件改由 shared 的 `./client` 供**:`src/client-entry.ts` 的运行期值导入(`diffTouched` 与卡片组件)并入 `./client`,`package.json` 新增 `dsh.bundle.client.external`,`build-client.mjs` 的外置清单同步——组件由模块图供给,不再在每个卡片产物里内联一份;`cordis.patch.yml` 因此插入一条**不写 id** 的 `@jayyuen66/dsh-plugin-shared` 组件行(写 id 会在插件页多出一个可关闭的组件,关掉后设置卡整块不激活)。
|
|
22
|
+
- **CI 的 pnpm 与 node 取值**:`ci.yml` 与 `publish.yml` 的 workflow 级 `env.PNPM_VERSION` 固定为 `12.11.2`(与伞根 `packageManager` 同源,浮动主版本 `12` 会让门禁与发布在补丁版之间漂移);`node: [22, 24]` 加 macOS 排除改为单格 `node: [24.9]`,`node-version` 由 `24` 改为 `24.9`。
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **注入回调不再丢掉 setup 屏障**(`host.ts`):`inject` 的回调返回类型由 `void` 放宽为 `void | Promise<void>`,把注册那一条 promise 交回 cordis——它把回调返回值当子 fiber 的 setup 屏障,返回 `void` 时页面策略那一条投影还没挂上就被判为装配完成。`test/host.test.ts` 补两条用例钉这一时序。
|
|
27
|
+
- **发布物体检在 Windows 上打不出包**(`pack-check.mjs`):`check:pack` 原先把系统临时目录的绝对路径带引号交给 ComSpec 的 `/c`,cmd 会把那对双引号连同参数一起交给 pnpm,pnpm 于是把 `"C:\Users\…\Temp\…"` 当相对路径拼到 cwd 上,报 `Failed to create directory D:\a\…\"C:\Users\…"`(os error 123)。目的地改为本包目录下一个自建的相对名(`.pack-tmp-<pid>-<rand>`):这一段名字既无空格也无引号,参数里没有需要保护的字符,两个平台同一种写法;`.gitignore` 补 `/.pack-tmp*/` 兜住进程被强杀时的残骸。同一条路径此前只在 Windows 格暴露——那一格上一版是 1.1 秒零输出的假绿。
|
|
28
|
+
- **Windows 格首次真跑出来的八条红**(`test/gate.test.ts`、`test/platform/guards.platform.test.ts`、`test/property/gate-invariants.property.test.ts`):`EditAccumulator.note()` 会 `path.normalize` 入参,Windows 上写死的 `/w/a.ts`、`src/a.ts` 归一成 `\` 形,而期望值是 POSIX 字形——断言因此只在半边平台成立。夹具改由 `path.join` 按当前平台构造,两侧同源后 normalize 成为恒等变换,被判的三件事(去重、淘汰顺序、扩展名过滤)没有变弱。`detectGate` 的 `root` 字面量不经归一,未动。
|
|
29
|
+
- **台账的同 callId 回填不再只否定最后一行**(`lib/edit-ledger.ts`):原先按 callId 找最近一条标记失败,普通编辑器一次调用只产一行、语义不变;`apply_patch` 一次调用可产多行,只否定最后一行会让其余路径在失败后仍被当成成功编辑,改为否定同 callId 的全部行。分流的 `pending` / `noCallId` 两张表合成一张 `records`,无 callId 的异常形状仍保守直记防漏。
|
|
30
|
+
- **回合累加器的编辑面补齐**(`lib/accumulator.ts`):`EDIT_TOOLS` 加入 `apply_patch`,多文件补丁的路径由 `editPathsOf` 逐枚展开,`note` 仍按单个路径去重。
|
|
31
|
+
|
|
32
|
+
### Docs
|
|
33
|
+
|
|
34
|
+
- dsh 徽章由 `^0.2.1-rc.3` 抬到 `^0.2.1-rc.5`,与 `engines.dsh` 首条同值;`cordis.patch.yml` 的注册行由「唯一一行」改写为「两条条目」并说明 shared 那行为何不写 id(`test/profile-bundle.ts` 里同类表述同步);开发面命令注释的 typecheck 由「三个 tsconfig」改为四个,与 `package.json` 的 `typecheck` 实跑数一致。
|
|
5
35
|
|
|
6
36
|
## 0.1.5
|
|
7
37
|
|
|
@@ -43,7 +73,7 @@
|
|
|
43
73
|
|
|
44
74
|
## 0.1.3
|
|
45
75
|
|
|
46
|
-
这一版把判定面与构建/CI 面一起收口:ruff 不再对只写了 `pyproject.toml` 的工程发门禁,卡片保存期间的编辑不再被静默丢弃,记忆反馈的文案与来源一致,Windows
|
|
76
|
+
这一版把判定面与构建/CI 面一起收口:ruff 不再对只写了 `pyproject.toml` 的工程发门禁,卡片保存期间的编辑不再被静默丢弃,记忆反馈的文案与来源一致,Windows 与发布链的静默降级补上判据。构建/CI 那一批清掉的是只在 Windows 上才会红的判据、两个构建脚本里没有任何门看得见的静默降级,以及发布链路缺的一道检查。宿主线抬到 `0.2.1-rc.3`。
|
|
47
77
|
|
|
48
78
|
### Fixed
|
|
49
79
|
|
|
@@ -54,12 +84,20 @@
|
|
|
54
84
|
- **四个工作流里的判断面缺陷**:`ci.yml` 的 `find … -print -quit` 在零匹配时得到空串、多匹配时静默取任意一个,改为「恰好一个 tarball」并点名实际个数;`publish.yml` 的重复发布检查把包名与依赖名写死在 URL 里(改名后会查到 404 而当成首发放行),改为从 `package.json` 读,并在版本低于 registry `latest` 时告警(只告警:从旧分支发版会把 `latest` 回退,但半吊子的预发布序比较不该一票否决发布);静态 token 的形态校验把 `exit 1` 降级为告警,OIDC 主线不该被兜底凭据的形态卡死;`scorecard.yml` 的 job 级判据用 `github.ref_name` 比默认分支名,同名 tag 上会误判,改为比完整 ref。
|
|
55
85
|
- **`ci.yml` 的 `-ignore SC2016` 是全局豁免**:一行调用同时校验四份工作流,`codeql.yml` 与 `scorecard.yml` 没有 run 脚本体,却也继承了这条豁免——它们将来真写出的同类缺陷会被一起吞掉。改成按文件分两次调用。
|
|
56
86
|
- **`codeql.yml` 与 `scorecard.yml` 缺并发控制与权限声明**:前者补 `actions: read`(本仓是 public,官方只在私有仓要求它;显式声明是为了可见性变化时不静默缺权限),后者补按事件名分组的 `concurrency`,让四个入口不再互相覆盖同一份 analysis 结果。
|
|
57
|
-
|
|
58
87
|
- **`test/schema-coverage.ts` 的 host schema 解析器**:括号匹配不跳过字符串、模板与注释,`Schema.object(` 只取首个块,扩展名只收 `.ts`,包根靠 `testDir/..` 硬推且读盘无兜底。这些偏差都会让「宿主字段全集」偏少而门禁假绿。解析器改成字符串/注释感知、遍历全部 `Schema.object`、按前缀识别扩展名、向上找 `package.json`、读不到时判红;新增 `test/schema-coverage.test.ts` 钉住这一面。
|
|
59
88
|
- **记忆反馈的目标面与正文收口**:网关地址必须是合法的 `http(s)` URL(半截串、非 http(s) scheme 均按「未配置」处理),凭据只发 https 或回环地址;`x-tdai-service-id` 形状不合规时回落默认服务名;命令、根与错误文本在写入记忆前折成单行,避免一条反馈被拆成多段。
|
|
60
89
|
- **编辑累加器的容量契约对任意入参成立**:`maxFiles` 为 NaN、0、负数或小数时回落到默认值;同一文件被反复编辑会刷新到最近位置,避免最该检查的文件第一个被淘汰;host 与累加器共用同一份编辑工具名单。
|
|
61
90
|
- **消息模板插值只认自有键**:`{constructor}` / `{toString}` 这类原型链名字不再渲染成函数源码,未提供的名字留空串。
|
|
62
91
|
|
|
92
|
+
构建与 CI 面(同属本版):
|
|
93
|
+
|
|
94
|
+
- **`host.ts` 里三处用 `/` 拼子项的地方改成 `path.join`。** `findManifestRoot` 探测 `MANIFESTS`、`workspaceRootUpgrade` 探测 `pnpm-workspace.yaml`、`probeProjectRoot` 过滤 manifest 与判断 `tsconfig.json`,原先都用模板串拼出 `dir/manifest`。`dir` 是宿主给的真实路径,在 Windows 上这种混形让 `existsSync` 恒假——于是所有 manifest 都探测不到、项目根一路回落到父目录,`gate-detect` 的判据整片失效而命令照常退出 0。同一处 `declaredScriptsOf` 读 `package.json` 也改成 `path.join`。新增 `TSCONFIG_MANIFEST` 常量,避免同一个文件名在文件里出现两次。
|
|
95
|
+
- **`collapseStorePaths` 的折叠原先完全静默。** 正则只认 `/` 分隔的「内容寻址 store 段」(`node_modules/` 下那层带「名@版_哈希」的目录),既没有归一反斜杠,也没有折掉前缀里的 `../`。没命中时它原样放行,于是 `client.js` 会带着「版本 + 本机 store 哈希」进公共产物,而两道新鲜度门禁比的都是同一台机器上的内存构建与磁盘文件——没有任何门能看见。现在先归一分隔符,再把可选的前缀与 store 段整段折成一层,折完仍检出 store 段就抛错停止构建。
|
|
96
|
+
- **`writeArtifact` 的临时文件名只用 pid。** 同一进程里并发写两个产物会撞名、各自截断对方。临时名加上 `randomUUID()`;`catch` 里清理临时文件的 `rm` 包进内层 try/catch,清理失败不再取代并吞掉真正的写失败(Windows 上 `EBUSY` / `EPERM` 会让清理自己抛)。
|
|
97
|
+
- **客户端产物的 `external` 由 `id.startsWith("react/")` 改成精确集合 `REACT_RUNTIME_MODULES`。** 外壳的模块表按精确串匹配,前缀放行会给产物留下模块表里没有的键——加载期报 `not in the module table`,整张卡片静默不渲染。集合与宿主客户端侧的 `PLATFORM_MODULES` 同源,只收宿主真给得出的四个说明符。
|
|
98
|
+
- **两处注释与实现不符。** 构建脚本的头注释说「react 及其子路径都要外部化」并给了「内联子路径会出现第二份 React」的理由——那个理由不成立,`react/jsx-runtime` 内联后自身仍 `require("react")`;`ci.yml` 里「门禁没跑过就别留下产物。」那行原先挂在上传覆盖率报告上方,与它描述的步骤错位。
|
|
99
|
+
- **`test/host-integrity.test.ts`、`test/host.test.ts`、`test/gateway-feedback.test.ts` 的 POSIX 假设。** 新增的 Windows 矩阵会让这些判据整片红:临时目录原先写成字面量 `/tmp/qg-shallow-…`,改成 `path.join(tmpdir(), …)`;匹配 tsc 命令的正则 `/bin\/tsc --noEmit$/u` 在 Windows 上拿到的渲染是 `'C:\…\bin\tsc' --noEmit`(带引号、反斜杠),改成 `/bin[\\/]tsc'? --noEmit$/u`;`deriveProjectKey` 的注入补上 `sep: "/"`,让它与字形的来源同源——路径字形与 `resolve` 不来自同一次调用时,`path.win32` 会把 `/tmp/x/proj` 解析成带盘符的串,字面量替换一次都不命中。
|
|
100
|
+
|
|
63
101
|
### Changed
|
|
64
102
|
|
|
65
103
|
- **宿主开发线抬到 `0.2.2-alpha.2`**:devDependencies 与 catalog/overrides 同值;`engines.dsh` 与
|
|
@@ -68,20 +106,11 @@
|
|
|
68
106
|
- **宿主线抬到 `0.2.1-rc.3`**:`engines.dsh`、peer/dev 依赖、overrides 与 README 徽标/正文同值。
|
|
69
107
|
- **构建编排统一进 shared 的 `lib/build-*`**:三个构建脚本只剩薄配置层,产物仍落 `dist/`;CI/CodeQL/Scorecard/Dependabot 与 Windows 矩阵补齐。
|
|
70
108
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
#### Fixed
|
|
76
|
-
|
|
77
|
-
- **`host.ts` 里三处用 `/` 拼子项的地方改成 `path.join`。** `findManifestRoot` 探测 `MANIFESTS`、`workspaceRootUpgrade` 探测 `pnpm-workspace.yaml`、`probeProjectRoot` 过滤 manifest 与判断 `tsconfig.json`,原先都用模板串拼出 `dir/manifest`。`dir` 是宿主给的真实路径,在 Windows 上这种混形让 `existsSync` 恒假——于是所有 manifest 都探测不到、项目根一路回落到父目录,`gate-detect` 的判据整片失效而命令照常退出 0。同一处 `declaredScriptsOf` 读 `package.json` 也改成 `path.join`。新增 `TSCONFIG_MANIFEST` 常量,避免同一个文件名在文件里出现两次。
|
|
78
|
-
- **`collapseStorePaths` 的折叠原先完全静默。** 正则只认 `/` 分隔的「内容寻址 store 段」(`node_modules/` 下那层带「名@版_哈希」的目录),既没有归一反斜杠,也没有折掉前缀里的 `../`。没命中时它原样放行,于是 `client.js` 会带着「版本 + 本机 store 哈希」进公共产物,而两道新鲜度门禁比的都是同一台机器上的内存构建与磁盘文件——没有任何门能看见。现在先归一分隔符,再把可选的前缀与 store 段整段折成一层,折完仍检出 store 段就抛错停止构建。
|
|
79
|
-
- **`writeArtifact` 的临时文件名只用 pid。** 同一进程里并发写两个产物会撞名、各自截断对方。临时名加上 `randomUUID()`;`catch` 里清理临时文件的 `rm` 包进内层 try/catch,清理失败不再取代并吞掉真正的写失败(Windows 上 `EBUSY` / `EPERM` 会让清理自己抛)。
|
|
80
|
-
- **客户端产物的 `external` 由 `id.startsWith("react/")` 改成精确集合 `REACT_RUNTIME_MODULES`。** 外壳的模块表按精确串匹配,前缀放行会给产物留下模块表里没有的键——加载期报 `not in the module table`,整张卡片静默不渲染。集合与宿主客户端侧的 `PLATFORM_MODULES` 同源,只收宿主真给得出的四个说明符。
|
|
81
|
-
- **两处注释与实现不符。** 构建脚本的头注释说「react 及其子路径都要外部化」并给了「内联子路径会出现第二份 React」的理由——那个理由不成立,`react/jsx-runtime` 内联后自身仍 `require("react")`;`ci.yml` 里「门禁没跑过就别留下产物。」那行原先挂在上传覆盖率报告上方,与它描述的步骤错位。
|
|
82
|
-
- **`test/host-integrity.test.ts`、`test/host.test.ts`、`test/gateway-feedback.test.ts` 的 POSIX 假设。** 新增的 Windows 矩阵会让这些判据整片红:临时目录原先写成字面量 `/tmp/qg-shallow-…`,改成 `path.join(tmpdir(), …)`;匹配 tsc 命令的正则 `/bin\/tsc --noEmit$/u` 在 Windows 上拿到的渲染是 `'C:\…\bin\tsc' --noEmit`(带引号、反斜杠),改成 `/bin[\\/]tsc'? --noEmit$/u`;`deriveProjectKey` 的注入补上 `sep: "/"`,让它与字形的来源同源——路径字形与 `resolve` 不来自同一次调用时,`path.win32` 会把 `/tmp/x/proj` 解析成带盘符的串,字面量替换一次都不命中。
|
|
109
|
+
- **`publish.yml` 补回 `pnpm run check`。** 发布由 `workflow_dispatch` 手动触发,可以从任意分支派发;CI 在 `main` 上的绿不代表被派发的那个 ref 是绿的,而发布前少这一道检查等于把「发布物能装」与「发布物正确」当成一件事。现在它在 `pnpm install --frozen-lockfile` 之后、`发布` 之前跑。
|
|
110
|
+
- **`ci.yml` 的覆盖率上传取 `if-no-files-found: warn` 而不是 `error`。** 这一步在 `if: always()` 下跑,而 `coverage/` 由 vitest 产出——typecheck / lint / build 任一先红时目录根本不存在,此时判红会盖住真正的失败原因,还把后面的步骤一起中断。
|
|
111
|
+
- **`ci.yml` 里新增的 Windows 矩阵与多平台并发组的形状**:组键带 `event_name`,取消只给 `push` 与 `pull_request`;gate 的检查名带 `matrix.os`;attest 拆成独立 job 并补齐 `artifact-metadata: write`;install-probe 收窄到 `contents: read`;所有 `actions/checkout` 带 `persist-credentials: false`;第三方 action 与 `actionlint` 全部钉到 commit SHA / 校验和。
|
|
83
112
|
|
|
84
|
-
|
|
113
|
+
### Added
|
|
85
114
|
|
|
86
115
|
- **`tsconfig.build-scripts.json` 与 `typecheck` 里的一次额外 `tsc`。** 两个构建脚本头部的 `// @ts-check` 只在编辑器里生效:它们不在任何 `tsc` program 的 `include` 里,CI 因此对这一面没有任何门。新 tsconfig 继承共享基线、打开 `allowJs` / `checkJs`、`noEmit`,只收两个构建脚本;`typecheck` 追加 `&& tsc --noEmit -p tsconfig.build-scripts.json`。变异实测:往 `build-client.mjs` 塞一行 `probe.toFixed("nope")` 会红,删掉即绿。
|
|
87
116
|
- **`tsconfig.build-scripts.json` 补齐之后暴露并修掉的类型缺陷**:`isRecord` 一律改成类型谓词(`@returns {raw is Record<string, unknown>}`),`asRecord` 改成守卫分支而不是类型断言(断言会同时撞 `typescript(no-unsafe-type-assertion)` 与 `eslint(no-inline-comments)`);`invokedDirectly()` 先取 `process.argv.at(1)` 再判 `undefined`,不再把可能不存在的值直接交给 `realpathSync`;读 `package.json` 的 `name` 用 `const nameKey = "name"` 索引——点号写法被 `noPropertyAccessFromIndexSignature` 拒绝,索引写法又被 `dot-notation` 要求改成点号,两侧只能靠一次具名取值同时满足。
|
|
@@ -89,18 +118,12 @@
|
|
|
89
118
|
- **`test/dist-size.test.ts` 的预算改成按实测锚定**:`host.js` 70 500 B(实测 61 110 B)、`client.js` 23 500 B(实测 20 222 B),取实测上浮约 15% 再向上取整到 500 B。注释写明本仓没有 `build-types.mjs`,所以只量两项。
|
|
90
119
|
- **`package.json` 的 `keywords`**,以及两份 README 各新增一节「shared 的版本下限 / Shared version floor」。
|
|
91
120
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
- **`publish.yml` 补回 `pnpm run check`。** 发布由 `workflow_dispatch` 手动触发,可以从任意分支派发;CI 在 `main` 上的绿不代表被派发的那个 ref 是绿的,而发布前少这一道检查等于把「发布物能装」与「发布物正确」当成一件事。现在它在 `pnpm install --frozen-lockfile` 之后、`发布` 之前跑。
|
|
95
|
-
- **`ci.yml` 的覆盖率上传取 `if-no-files-found: warn` 而不是 `error`。** 这一步在 `if: always()` 下跑,而 `coverage/` 由 vitest 产出——typecheck / lint / build 任一先红时目录根本不存在,此时判红会盖住真正的失败原因,还把后面的步骤一起中断。
|
|
96
|
-
- **`ci.yml` 里新增的 Windows 矩阵与多平台并发组的形状**:组键带 `event_name`,取消只给 `push` 与 `pull_request`;gate 的检查名带 `matrix.os`;attest 拆成独立 job 并补齐 `artifact-metadata: write`;install-probe 收窄到 `contents: read`;所有 `actions/checkout` 带 `persist-credentials: false`;第三方 action 与 `actionlint` 全部钉到 commit SHA / 校验和。
|
|
97
|
-
|
|
98
|
-
#### Not changed, deliberately
|
|
121
|
+
### Not changed, deliberately
|
|
99
122
|
|
|
100
123
|
- **除 ruff 的可发条件外,四条命令的白名单与优先级、`not-run` 与 `code-failure` 的分界、注入配额、预算分摊、默认设置值与双语文案内容未变。**
|
|
101
124
|
- **覆盖率阈值仍由 `@jayyuen66/dsh-plugin-shared/config/vitest.base` 单源给出(100%)**,`coverageInclude` 仍只覆盖 host 半。
|
|
102
125
|
|
|
103
|
-
|
|
126
|
+
### Tests
|
|
104
127
|
|
|
105
128
|
- `pnpm run check` 通过:typecheck 三个 tsconfig、`oxlint` 0 warning / 0 error、两个 rolldown 构建、`vitest run --coverage` 四项 100%、`oxfmt --check`、`check:pack` 的 tarball 体检。
|
|
106
129
|
- 新增的门禁都做过变异实测:`tsconfig.build-scripts.json` 那条塞错误即红、删掉即绿;`collapseStorePaths` 的残留检查拿含 `.pnpm` 的行喂进去会抛,四种真实形态(无前缀 / `../` 前缀 / 反斜杠 / 普通相对段)折叠结果逐个断言。
|
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<a href="https://www.npmjs.com/package/@jayyuen66/dsh-quality-gate"><img src="https://img.shields.io/npm/d18m/@jayyuen66/dsh-quality-gate?style=flat-square" alt="npm downloads (total)" /></a>
|
|
12
12
|
<a href="https://github.com/JayYuen666/dsh-quality-gate/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/JayYuen666/dsh-quality-gate/ci.yml?branch=main&style=flat-square&label=ci" alt="ci" /></a>
|
|
13
13
|
<img src="https://img.shields.io/badge/license-MIT-4c1?style=flat-square" alt="MIT" />
|
|
14
|
-
<img src="https://img.shields.io/badge/node-%3E%
|
|
15
|
-
<img src="https://img.shields.io/badge/dsh-%5E0.2.1--rc.
|
|
14
|
+
<img src="https://img.shields.io/badge/node-%3E%3D24.9-4c1?style=flat-square&logo=nodedotjs&logoColor=white" alt="node" />
|
|
15
|
+
<img src="https://img.shields.io/badge/dsh-%5E0.2.1--rc.5-658EFF?style=flat-square" alt="dsh" />
|
|
16
16
|
<a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/powered_by_dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white" alt="powered by dsh" /></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
@@ -61,8 +61,8 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
61
61
|
|
|
62
62
|
Remove with `dsh plugin --profile web remove @jayyuen66/dsh-quality-gate`. The package is on the public npm registry, so installation needs no credentials.
|
|
63
63
|
|
|
64
|
-
- **Requires Node.js `>=
|
|
65
|
-
- **Requires a host `dsh` on the `0.2.1-rc.
|
|
64
|
+
- **Requires Node.js `>= 24.9`.** The artifacts use newer runtime faces such as `AbortSignal.any`.
|
|
65
|
+
- **Requires a host `dsh` on the `0.2.1-rc.5` or `0.2.2-alpha.3` line.** The requirement the host reads is `engines.dsh` (`^0.2.1-rc.5 || ^0.2.2-alpha.3`), and it **decides alone**; pre-release versions participate in range comparison, and `0.3.0` and later need the explicit, risk-accepting grant `dsh plugin --profile <p> allow-version <pkg>@<ver> --dsh-version <exact> --accept-risk`. The support window rolls: this package is verified against every released host version inside the window at publish time and re-verified once the window moves, so the version number in this bullet is not a standing promise.
|
|
66
66
|
- **The runtime dependency `@jayyuen66/dsh-plugin-shared` must already be on the same registry**, otherwise the whole set 404s.
|
|
67
67
|
- **`typescript` is a real `dependencies` entry**: the fallback entry for the `tsc` gate — the project's own install wins, then the copy shipped with this package; no network action at runtime, never an `npx` auto-download.
|
|
68
68
|
- **`zod` is also a real `dependencies` entry**: the edit-ledger projection state and the settings card's value/draft faces use it for shape validation, versioned through the umbrella catalog. The client artifact keeps `require("zod")` externalized via the host's `PLATFORM_MODULES`.
|
|
@@ -91,7 +91,7 @@ If your profile pins `@jayyuen66/dsh-plugin-shared` with a pnpm `overrides` entr
|
|
|
91
91
|
|
|
92
92
|
## <img src="https://api.iconify.design/lucide/plug.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> Enabling it in dsh
|
|
93
93
|
|
|
94
|
-
- Bundle form: `dsh.bundle.patch` in `package.json` points at `cordis.patch.yml`,
|
|
94
|
+
- Bundle form: `dsh.bundle.patch` in `package.json` points at `cordis.patch.yml`, which carries **two entries**: `- name: "@jayyuen66/dsh-plugin-shared"` (the supplier of the card components, deliberately **without an `id`** so the plugin page does not list it as a switchable component) and `- id: quality-gate` with `name` set to the bare package name (the host scans `dsh.client` only on bare-name entries); `dsh plugin --profile web add/remove` maintains those lines.
|
|
95
95
|
- The host half hard-depends only on `settings` (`default.inject = ["settings"]`); precedence is card runtime value > the line's `config:` > schema built-in defaults.
|
|
96
96
|
- The client half (`dsh.client.platform = web`, `immediately: true`) injects a card into the `plugins.bundle.config` slot of the plugin page. Two identifiers, deliberately different sources: the slot key is the **bundle package name** `@jayyuen66/dsh-quality-gate`, while the settings namespace and `configForms.get()` use the **bare entry id** `quality-gate` — mix them up and the card never renders.
|
|
97
97
|
- Saving on the card is explicitly staged: edits stay in local pending state until Save writes them **serially**, key by key, consuming the `Promise<boolean>` acceptance bit — the first `false` stops the loop, shows `saveRejected` and keeps the unwritten changes; a rejecting write shows `saveFailed`. Inputs stay editable during a save, and edits made in flight stay in the pending set instead of being wiped by it; Revert discards the pending edits. Clearing an input means unset, restoring that field's schema default.
|
|
@@ -156,7 +156,7 @@ Settings persist in the host's settings file under the dsh data directory; the c
|
|
|
156
156
|
## <img src="https://api.iconify.design/lucide/triangle-alert.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> Known boundaries
|
|
157
157
|
|
|
158
158
|
- **The whitelist has exactly four commands and cannot be extended via settings.** Roots checked by other tools (`go test`, `mypy`, `eslint`, …) get no gate.
|
|
159
|
-
- **Only successful edits count**, and only writes through `edit` / `write` / `str_replace_editor` (`view` is read-only). Files outside the extension whitelist (md/txt/images/`.sh`) are not counted — none of the gate commands can validate them, and a `.sh` would only drag the root resolution somewhere else and produce a false failure.
|
|
159
|
+
- **Only successful edits count**, and only writes through `edit` / `write` / `str_replace_editor` / `apply_patch` (`view` is read-only). A multi-file `apply_patch` records every operation target, including both sides of a move. Files outside the extension whitelist (md/txt/images/`.sh`) are not counted — none of the gate commands can validate them, and a `.sh` would only drag the root resolution somewhere else and produce a false failure.
|
|
160
160
|
- **Out-of-process subagents (ACP / claude-code / codex) emit no dsh events**, so they are outside the gate by construction; in-process subagents are skipped too, and their edits land in their own session ledger rather than the parent's — there is currently no guarantee that the parent turn verifies them (they can be missed).
|
|
161
161
|
- **Never talks over the user**: no injection when the inbox has pending items, the turn signal is aborted, or the plugin is unloading. Plan mode is not exempt — a planning turn that touched code is gated the same way.
|
|
162
162
|
- **When evidence is truncated with no spill file, the first error may already be gone**; the text says the evidence is incomplete instead of pretending otherwise.
|
|
@@ -177,7 +177,7 @@ Settings persist in the host's settings file under the dsh data directory; the c
|
|
|
177
177
|
|
|
178
178
|
```sh
|
|
179
179
|
pnpm install
|
|
180
|
-
pnpm run check # typecheck (
|
|
180
|
+
pnpm run check # typecheck (four tsconfigs) → lint → client build → host build → tests (with coverage) → tarball inspection → format
|
|
181
181
|
```
|
|
182
182
|
|
|
183
183
|
- Coverage is measured over `host.ts`, `lib/**` and `src/ui-messages.ts`, with all four thresholds at 100%. The dictionary is plain data driven headlessly by `test/client-card.test.ts`, so counting it keeps the threshold on the part that is fully verified; `client-entry.ts` is excluded because most of it is the render surface, which needs a DOM harness this package deliberately omits (`react-dom`/`jsdom` are not pulled in). Counting it would turn the thresholds into a permanent red.
|
package/README.zh-CN.md
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<a href="https://www.npmjs.com/package/@jayyuen66/dsh-quality-gate"><img src="https://img.shields.io/npm/d18m/@jayyuen66/dsh-quality-gate?style=flat-square" alt="npm downloads (total)" /></a>
|
|
12
12
|
<a href="https://github.com/JayYuen666/dsh-quality-gate/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/JayYuen666/dsh-quality-gate/ci.yml?branch=main&style=flat-square&label=ci" alt="ci" /></a>
|
|
13
13
|
<img src="https://img.shields.io/badge/license-MIT-4c1?style=flat-square" alt="MIT" />
|
|
14
|
-
<img src="https://img.shields.io/badge/node-%3E%
|
|
15
|
-
<img src="https://img.shields.io/badge/dsh-%5E0.2.1--rc.
|
|
14
|
+
<img src="https://img.shields.io/badge/node-%3E%3D24.9-4c1?style=flat-square&logo=nodedotjs&logoColor=white" alt="node" />
|
|
15
|
+
<img src="https://img.shields.io/badge/dsh-%5E0.2.1--rc.5-658EFF?style=flat-square" alt="dsh" />
|
|
16
16
|
<a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/powered_by_dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white" alt="powered by dsh" /></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
@@ -61,8 +61,8 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
61
61
|
|
|
62
62
|
卸载:`dsh plugin --profile web remove @jayyuen66/dsh-quality-gate`。包在公共 npm 上,安装不需要凭据,也不需要配作用域源。
|
|
63
63
|
|
|
64
|
-
- **要求 Node.js `>=
|
|
65
|
-
- **要求宿主 `dsh` 在 `0.2.1-rc.
|
|
64
|
+
- **要求 Node.js `>= 24.9`。** 产物用到 `AbortSignal.any` 这类较新的运行面。
|
|
65
|
+
- **要求宿主 `dsh` 在 `0.2.1-rc.5` 或 `0.2.2-alpha.3` 这条线上。** 宿主读取的版本要求是 `engines.dsh`(`^0.2.1-rc.5 || ^0.2.2-alpha.3`),它**单独决定**兼容性;预发布版本参与范围比较,`0.3.0` 及以后要走显式担责的授权 `dsh plugin --profile <p> allow-version <pkg>@<ver> --dsh-version <exact> --accept-risk`。支持窗口是滚动的:本包在发布时把窗口内的每个已发布宿主版本逐版取证,窗口滚动后重新取证,不把这一句里的版本号当长期承诺。
|
|
66
66
|
- **运行时依赖 `@jayyuen66/dsh-plugin-shared` 必须已在同一 registry 上**,否则整组 404。
|
|
67
67
|
- **`typescript` 是真实的 `dependencies`**:`tsc` 门禁的兜底入口——工程自己装的那份优先,其次随包安装的这份,运行期无网络动作,从不走 `npx` 的自动下载。
|
|
68
68
|
- **`zod` 也是真实的 `dependencies`**:编辑台账投影状态与设置卡的值面/草稿面用它校验形状;版本走伞根 catalog。client 产物按宿主 `PLATFORM_MODULES` 保持 `require("zod")` 外部化。
|
|
@@ -93,7 +93,7 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
93
93
|
|
|
94
94
|
## <img src="https://api.iconify.design/lucide/plug.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 在 dsh 里启用
|
|
95
95
|
|
|
96
|
-
- 组合包形态:`package.json` 的 `dsh.bundle.patch` 指向 `cordis.patch.yml
|
|
96
|
+
- 组合包形态:`package.json` 的 `dsh.bundle.patch` 指向 `cordis.patch.yml`,里面是**两条条目**:`- name: "@jayyuen66/dsh-plugin-shared"`(卡片组件的供应行,**刻意不写 `id`**,所以插件页不会把它列成可停用组件)与 `- id: quality-gate`、`name` 为裸包名(宿主只对裸包名条目扫 `dsh.client`);`dsh plugin --profile web add/remove` 维护这两行。
|
|
97
97
|
- Host 半硬依赖只有 `settings`(`default.inject = ["settings"]`);优先级是 设置卡运行时值 > 行 `config:` > schema 内置默认。
|
|
98
98
|
- Client 半(`dsh.client.platform = web`、`immediately: true`)往插件管理页的 `plugins.bundle.config` 槽注入一张卡。两枚标识刻意不同源:槽位 key 是 **bundle 包名** `@jayyuen66/dsh-quality-gate`,设置命名空间与 `configForms.get()` 用的是**裸条目 id** `quality-gate`——写混了卡片哪都不渲染。
|
|
99
99
|
- 卡的保存是显式两段式:改动先留在本地暂存(状态条提示),点「保存」才逐键**串行**写入并消费 `Promise<boolean>` 受理位——首个 `false` 即停手、显示 `saveRejected` 并保留未落盘的改动;写入 reject 显示 `saveFailed`。保存期间输入框仍可编辑,期间产生的新改动保留在待保存面里,不会被这次保存顺带抹掉;「撤销」丢弃暂存。清空一个输入框 = unset,恢复该字段的 schema 默认。
|
|
@@ -158,7 +158,7 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
158
158
|
## <img src="https://api.iconify.design/lucide/triangle-alert.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 已知边界
|
|
159
159
|
|
|
160
160
|
- **白名单只有四条命令,不能经设置扩展。** 用别的检查器(`go test`、`mypy`、`eslint`…)的根得不到门禁。
|
|
161
|
-
- **只记成功编辑**,且只认 `edit` / `write` / `str_replace_editor` 的写入(`view`
|
|
161
|
+
- **只记成功编辑**,且只认 `edit` / `write` / `str_replace_editor` / `apply_patch` 的写入(`view` 只读不算)。多文件 `apply_patch` 按每个操作目标分别记账,move 的源与目的都算。扩展名白名单之外的文件(md/txt/图片/`.sh`)不计——没有一条门禁命令能校验它们,`.sh` 只会把根解析到别处跑出假失败。
|
|
162
162
|
- **out-of-process 子代理(ACP / claude-code / codex)不发 dsh 事件**,天然不在门禁面内;in-process 子代理的回合也跳过门禁,且其编辑落在各自会话的台账,不进入父会话的编辑台账——当前不保证被父会话核验(可能漏检)。
|
|
163
163
|
- **不抢话**:inbox 已有待处理消息、回合信号已取消或插件已卸载时不注入。plan mode 不豁免——规划回合改了代码同样收口。
|
|
164
164
|
- **证据被截断且没有落盘文件时,首个错误可能已随截断丢弃**;文案如实说明证据不完整,不假装完整。
|
|
@@ -179,7 +179,7 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
179
179
|
|
|
180
180
|
```sh
|
|
181
181
|
pnpm install
|
|
182
|
-
pnpm run check # typecheck
|
|
182
|
+
pnpm run check # typecheck(四个 tsconfig)→ lint → client 构建 → host 构建 → 测试(含覆盖率)→ tarball 体检 → 格式
|
|
183
183
|
```
|
|
184
184
|
|
|
185
185
|
- 覆盖率统计 `host.ts`、`lib/**` 与 `src/ui-messages.ts`,四项阈值 100%。字典是纯数据、`test/client-card.test.ts` 无头就能跑满,计进分母才能让阈值守住它;`client-entry.ts` 不计——它的大头是渲染面,要 DOM 基座才能测,本包刻意不引 `react-dom` 与 `jsdom`,整体计进来会让阈值变成常红。
|
package/client.js
CHANGED
|
@@ -8,352 +8,7 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
9
9
|
let react = require("react");
|
|
10
10
|
let zod = require("zod");
|
|
11
|
-
|
|
12
|
-
function e$1(t, n, r) {
|
|
13
|
-
if (t == null || n == null) return (t ?? null) === (n ?? null);
|
|
14
|
-
if (Object.is(t, n)) return !0;
|
|
15
|
-
if (typeof t != `object` || typeof n != `object`) return !1;
|
|
16
|
-
if (r.get(t) === n) return !0;
|
|
17
|
-
if (r.set(t, n), Array.isArray(t) || Array.isArray(n)) return !Array.isArray(t) || !Array.isArray(n) || t.length !== n.length ? !1 : t.every((t, i) => e$1(t, n[i], r));
|
|
18
|
-
let i = Object.keys(t), a = Object.keys(n);
|
|
19
|
-
return i.length === a.length && i.every((i) => Object.hasOwn(n, i) && e$1(Reflect.get(t, i), Reflect.get(n, i), r));
|
|
20
|
-
}
|
|
21
|
-
function t$1(t, n) {
|
|
22
|
-
let r = [];
|
|
23
|
-
for (let i of Object.keys(t)) e$1(t[i], n[i], /* @__PURE__ */ new WeakMap()) || r.push(i);
|
|
24
|
-
return r;
|
|
25
|
-
}
|
|
26
|
-
// node_modules/@jayyuen66/dsh-plugin-shared/dist/card-ui.js
|
|
27
|
-
const c = `
|
|
28
|
-
.dsu-form{display:flex;flex-direction:column}
|
|
29
|
-
.dsu-section{margin-top:24px;padding-top:24px;border-top:.5px solid var(--dsw-alias-border-l1)}
|
|
30
|
-
.dsu-form>.dsu-section:first-child{margin-top:0;padding-top:0;border-top:0}
|
|
31
|
-
.dsu-sectionTitle{margin:0;font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary)}
|
|
32
|
-
.dsu-sectionHint{margin:4px 0 0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
|
|
33
|
-
.dsu-statusbar{display:flex;flex-wrap:wrap;gap:4px 8px;margin-top:8px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
|
|
34
|
-
.dsu-field{display:grid;grid-template-columns:minmax(0,1fr) minmax(240px,360px);column-gap:32px;align-items:center;padding:12px 0;border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
35
|
-
.dsu-fieldMeta{display:flex;flex-direction:column;gap:4px;min-width:0}
|
|
36
|
-
.dsu-labelRow{display:flex;align-items:center;gap:6px;min-width:0}
|
|
37
|
-
.dsu-label{font-size:13px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary)}
|
|
38
|
-
.dsu-labelMono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}
|
|
39
|
-
.dsu-hint{margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
|
|
40
|
-
.dsu-control{min-width:0;display:flex;align-items:center;gap:8px}
|
|
41
|
-
.dsu-fieldWide{grid-template-columns:minmax(0,1fr);align-items:start}
|
|
42
|
-
.dsu-fieldWide .dsu-control{width:100%;max-width:720px;margin-top:2px}
|
|
43
|
-
.dsu-help{display:inline-flex;align-items:center;gap:6px;min-width:0}
|
|
44
|
-
.dsu-helpBtn{appearance:none;border:0;background:transparent;padding:0;width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary);cursor:help;border-radius:50%;flex:none}
|
|
45
|
-
.dsu-help:hover .dsu-helpBtn,.dsu-helpBtn:focus-visible{color:var(--dsw-alias-label-primary)}
|
|
46
|
-
.dsu-helpBtn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
|
|
47
|
-
.dsu-tip{position:fixed;z-index:60;box-sizing:border-box;max-width:min(340px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:auto;overflow-wrap:anywhere;padding:10px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);box-shadow:0 12px 32px rgb(0 0 0 / 45%);font-size:12px;line-height:1.6;font-weight:400;color:var(--dsw-alias-label-secondary)}
|
|
48
|
-
.dsu-input{width:100%;height:34px;padding:0 12px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}
|
|
49
|
-
.dsu-input:focus-visible{outline:none;border-color:var(--dsw-alias-state-business-primary)}
|
|
50
|
-
.dsu-input[aria-invalid="true"]{border-color:var(--dsw-alias-state-error-primary)}
|
|
51
|
-
.dsu-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
52
|
-
.dsu-input::placeholder{color:var(--dsw-alias-label-tertiary)}
|
|
53
|
-
.dsu-textarea{height:auto;min-height:96px;padding:8px 12px;line-height:1.6;resize:vertical}
|
|
54
|
-
.dsu-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
|
|
55
|
-
.dsu-numberWrap{display:flex;flex-direction:column;gap:4px;width:100%;min-width:0}
|
|
56
|
-
.dsu-number{display:flex;align-items:center;gap:8px;min-width:0}
|
|
57
|
-
.dsu-number .dsu-input{min-width:0}
|
|
58
|
-
.dsu-unit{flex:none;font-size:12px;color:var(--dsw-alias-label-tertiary)}
|
|
59
|
-
.dsu-error{margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary)}
|
|
60
|
-
.dsu-switch{appearance:none;width:36px;height:20px;border-radius:999px;border:0;padding:2px;background:var(--dsw-alias-border-l3);display:flex;align-items:center;cursor:pointer;flex:none;transition:background-color .16s}
|
|
61
|
-
.dsu-knob{width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-label-primary);transition:transform .16s,background-color .16s}
|
|
62
|
-
.dsu-switch[aria-checked="true"]{background:var(--dsw-alias-label-primary)}
|
|
63
|
-
.dsu-switch[aria-checked="true"] .dsu-knob{transform:translateX(16px);background:var(--dsw-alias-label-primary-foreground)}
|
|
64
|
-
.dsu-switch:disabled{opacity:.4;cursor:not-allowed}
|
|
65
|
-
.dsu-switch:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
|
|
66
|
-
.dsu-selectWrap{position:relative;width:100%;min-width:0}
|
|
67
|
-
.dsu-select{display:flex;align-items:center;justify-content:space-between;gap:8px;text-align:left;cursor:pointer}
|
|
68
|
-
.dsu-select[aria-expanded="true"]{border-color:var(--dsw-alias-state-business-primary)}
|
|
69
|
-
.dsu-selectValue{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
70
|
-
.dsu-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s}
|
|
71
|
-
.dsu-select[aria-expanded="true"] .dsu-chevron{transform:rotate(180deg)}
|
|
72
|
-
.dsu-menu{position:fixed;z-index:70;padding:4px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);border-radius:12px;box-shadow:0 12px 32px rgb(0 0 0 / 50%);max-height:280px;overflow:auto}
|
|
73
|
-
.dsu-option{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:1.4;width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 10px;border-radius:8px;cursor:pointer;text-align:left}
|
|
74
|
-
.dsu-option:hover,.dsu-option[data-active="true"]{background:var(--dsw-alias-bg-layer-3)}
|
|
75
|
-
.dsu-option:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed}
|
|
76
|
-
.dsu-option:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}
|
|
77
|
-
.dsu-optionGroup{padding:6px 10px 2px;font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary)}
|
|
78
|
-
.dsu-check{opacity:0;flex:none}
|
|
79
|
-
.dsu-option[aria-selected="true"] .dsu-check{opacity:1}
|
|
80
|
-
.dsu-savebar{display:flex;align-items:center;gap:8px;padding:12px 0;margin-top:24px;border-top:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base)}
|
|
81
|
-
.dsu-savebar[data-dirty="true"]{position:sticky;bottom:0;z-index:2;box-shadow:0 -12px 24px rgb(0 0 0 / 35%)}
|
|
82
|
-
.dsu-btn{appearance:none;border:.5px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:1.5;border-radius:var(--dsw-radius-md);padding:5px 14px;cursor:pointer;transition:background-color .12s,border-color .12s,color .12s,filter .12s}
|
|
83
|
-
.dsu-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l3)}
|
|
84
|
-
.dsu-btn:active:not(:disabled){background:var(--dsw-alias-bg-layer-1)}
|
|
85
|
-
.dsu-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
|
|
86
|
-
.dsu-btn:disabled{opacity:.4;cursor:not-allowed}
|
|
87
|
-
.dsu-btnPrimary{background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
|
|
88
|
-
.dsu-btnPrimary:hover:not(:disabled){background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);filter:brightness(.9)}
|
|
89
|
-
.dsu-btnPrimary:active:not(:disabled){background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);filter:brightness(.84)}
|
|
90
|
-
.dsu-btnDanger{color:var(--dsw-alias-state-error-primary)}
|
|
91
|
-
.dsu-btnDanger:hover:not(:disabled){color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2)}
|
|
92
|
-
.dsu-status{margin-left:auto;font-size:12px;color:var(--dsw-alias-label-tertiary)}
|
|
93
|
-
.dsu-statusError{color:var(--dsw-alias-state-error-primary)}
|
|
94
|
-
.dsu-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr));gap:0 24px}
|
|
95
|
-
.dsu-grid>.dsu-field{display:flex;flex-direction:column;align-items:stretch;gap:6px;padding:10px 0;border-top:.5px solid var(--dsw-alias-border-l1)}
|
|
96
|
-
.dsu-grid>.dsu-field .dsu-control{width:100%}
|
|
97
|
-
@media (max-width:640px){
|
|
98
|
-
.dsu-field{grid-template-columns:minmax(0,1fr);align-items:start}
|
|
99
|
-
.dsu-control{width:100%;max-width:720px}
|
|
100
|
-
.dsu-section{margin-top:20px;padding-top:20px}
|
|
101
|
-
}
|
|
102
|
-
@media (prefers-reduced-motion:reduce){
|
|
103
|
-
.dsu-switch,.dsu-knob,.dsu-chevron,.dsu-btn{transition:none}
|
|
104
|
-
}
|
|
105
|
-
`;
|
|
106
|
-
function l(e, t = 30) {
|
|
107
|
-
return e.length <= t ? { hint: e } : { detail: e };
|
|
108
|
-
}
|
|
109
|
-
function u(t) {
|
|
110
|
-
return (0, react.createElement)(`div`, { className: `dsu-form` }, t.children);
|
|
111
|
-
}
|
|
112
|
-
function d(t) {
|
|
113
|
-
return (0, react.createElement)(`section`, { className: `dsu-section` }, (0, react.createElement)(`h2`, { className: `dsu-sectionTitle` }, t.title), t.hint === void 0 ? null : (0, react.createElement)(`p`, { className: `dsu-sectionHint` }, t.hint), t.children);
|
|
114
|
-
}
|
|
115
|
-
function m(a) {
|
|
116
|
-
let c = (0, react.useId)(), [l, u] = (0, react.useState)(!1), [d, f] = (0, react.useState)(!1), p = (0, react.useRef)(!1), m = (0, react.useRef)(!1), h = (0, react.useRef)(!1), g = (0, react.useRef)(null), _ = (0, react.useRef)(null), v = (0, react.useRef)(null), y = (0, react.useCallback)(() => {
|
|
117
|
-
v.current !== null && (clearTimeout(v.current), v.current = null);
|
|
118
|
-
}, []), b = (0, react.useCallback)(() => {
|
|
119
|
-
let e = g.current, t = _.current;
|
|
120
|
-
/* v8 ignore next 3 -- place 只在 open 的 layout effect 与挂载期监听器里调用;refs 为空只可能是卸载竞态 */
|
|
121
|
-
if (e === null || t === null) return;
|
|
122
|
-
let n = e.getBoundingClientRect(), r = t.offsetWidth, i = t.offsetHeight, { left: a } = n, o = n.bottom + 8;
|
|
123
|
-
o + i > window.innerHeight - 8 && (o = n.top - i - 8), a + r > window.innerWidth - 8 && (a = window.innerWidth - r - 8);
|
|
124
|
-
let s = Math.max(8, window.innerWidth - r - 8), c = Math.max(8, window.innerHeight - i - 8);
|
|
125
|
-
t.style.left = `${Math.min(Math.max(8, a), s)}px`, t.style.top = `${Math.min(Math.max(8, o), c)}px`;
|
|
126
|
-
}, []), x = (0, react.useCallback)(() => {
|
|
127
|
-
y(), h.current = !1, f(!1), u(!1);
|
|
128
|
-
}, [y]), S = (0, react.useCallback)(() => {
|
|
129
|
-
y(), u(!0);
|
|
130
|
-
}, [y]), C = (0, react.useCallback)(() => {
|
|
131
|
-
y(), !(p.current || m.current || h.current) && (v.current = setTimeout(() => {
|
|
132
|
-
/* v8 ignore next 3 -- 任何重新进入/钉住都会 cancelHide;这次复查只防未来的异步路径 */
|
|
133
|
-
v.current = null, p.current || m.current || h.current || u(!1);
|
|
134
|
-
}, 120));
|
|
135
|
-
}, [y]), w = (0, react.useCallback)((e) => {
|
|
136
|
-
h.current = e, f(e);
|
|
137
|
-
}, []);
|
|
138
|
-
(0, react.useLayoutEffect)(() => {
|
|
139
|
-
if (!l) return () => {};
|
|
140
|
-
b();
|
|
141
|
-
let e = (e) => {
|
|
142
|
-
let { target: t } = e;
|
|
143
|
-
t instanceof Node && _.current?.contains(t) === !0 || (h.current ? b() : x());
|
|
144
|
-
}, t = () => {
|
|
145
|
-
b();
|
|
146
|
-
};
|
|
147
|
-
return window.addEventListener(`scroll`, e, !0), window.addEventListener(`resize`, t), () => {
|
|
148
|
-
window.removeEventListener(`scroll`, e, !0), window.removeEventListener(`resize`, t);
|
|
149
|
-
};
|
|
150
|
-
}, [
|
|
151
|
-
l,
|
|
152
|
-
a.detail,
|
|
153
|
-
b,
|
|
154
|
-
x
|
|
155
|
-
]), (0, react.useEffect)(() => {
|
|
156
|
-
if (!l) return () => {};
|
|
157
|
-
let e = (e) => {
|
|
158
|
-
e.key === `Escape` && x();
|
|
159
|
-
};
|
|
160
|
-
return document.addEventListener(`keydown`, e), () => {
|
|
161
|
-
document.removeEventListener(`keydown`, e);
|
|
162
|
-
};
|
|
163
|
-
}, [l, x]), (0, react.useEffect)(() => y, [y]);
|
|
164
|
-
let T = (0, react.createElement)(`button`, {
|
|
165
|
-
ref: g,
|
|
166
|
-
type: `button`,
|
|
167
|
-
className: `dsu-helpBtn`,
|
|
168
|
-
"aria-label": `${a.label} 说明`,
|
|
169
|
-
"aria-describedby": c,
|
|
170
|
-
"aria-pressed": d,
|
|
171
|
-
onClick: () => {
|
|
172
|
-
h.current ? x() : (w(!0), S());
|
|
173
|
-
}
|
|
174
|
-
}, (0, react.createElement)(`svg`, {
|
|
175
|
-
width: 12,
|
|
176
|
-
height: 12,
|
|
177
|
-
viewBox: `0 0 14 14`,
|
|
178
|
-
"aria-hidden": !0
|
|
179
|
-
}, (0, react.createElement)(`circle`, {
|
|
180
|
-
cx: 7,
|
|
181
|
-
cy: 7,
|
|
182
|
-
r: 6,
|
|
183
|
-
fill: `none`,
|
|
184
|
-
stroke: `currentColor`,
|
|
185
|
-
strokeWidth: 1.2
|
|
186
|
-
}), (0, react.createElement)(`path`, {
|
|
187
|
-
d: `M7 6.2v4`,
|
|
188
|
-
stroke: `currentColor`,
|
|
189
|
-
strokeWidth: 1.3,
|
|
190
|
-
strokeLinecap: `round`
|
|
191
|
-
}), (0, react.createElement)(`circle`, {
|
|
192
|
-
cx: 7,
|
|
193
|
-
cy: 4.3,
|
|
194
|
-
r: .8,
|
|
195
|
-
fill: `currentColor`
|
|
196
|
-
}))), E = (0, react.createElement)(`div`, {
|
|
197
|
-
ref: _,
|
|
198
|
-
id: c,
|
|
199
|
-
role: `tooltip`,
|
|
200
|
-
className: `dsu-tip`,
|
|
201
|
-
hidden: !l
|
|
202
|
-
}, a.detail), D = a.showLabel === !1 ? null : (0, react.createElement)(`span`, { className: `dsu-label${a.compact === !0 ? ` dsu-labelMono` : ``}` }, a.label);
|
|
203
|
-
return (0, react.createElement)(`span`, {
|
|
204
|
-
className: `dsu-help`,
|
|
205
|
-
onMouseEnter: () => {
|
|
206
|
-
p.current = !0, S();
|
|
207
|
-
},
|
|
208
|
-
onMouseLeave: () => {
|
|
209
|
-
p.current = !1, C();
|
|
210
|
-
},
|
|
211
|
-
onFocus: () => {
|
|
212
|
-
m.current = !0, S();
|
|
213
|
-
},
|
|
214
|
-
onBlur: (e) => {
|
|
215
|
-
let t = e.relatedTarget;
|
|
216
|
-
t instanceof Node && e.currentTarget.contains(t) || (m.current = !1, C());
|
|
217
|
-
}
|
|
218
|
-
}, D, T, E);
|
|
219
|
-
}
|
|
220
|
-
function h(t) {
|
|
221
|
-
let n = `dsu-label${t.compact === !0 ? ` dsu-labelMono` : ``}`, r = t.controlId === void 0 ? (0, react.createElement)(`span`, { className: n }, t.label) : (0, react.createElement)(`label`, {
|
|
222
|
-
className: n,
|
|
223
|
-
htmlFor: t.controlId
|
|
224
|
-
}, t.label);
|
|
225
|
-
return (0, react.createElement)(`div`, { className: `dsu-labelRow` }, r, t.detail === void 0 ? null : (0, react.createElement)(m, {
|
|
226
|
-
label: t.label,
|
|
227
|
-
detail: t.detail,
|
|
228
|
-
showLabel: !1
|
|
229
|
-
}));
|
|
230
|
-
}
|
|
231
|
-
function g(t) {
|
|
232
|
-
let n = t.controlId === void 0 ? void 0 : `${t.controlId}-hint`;
|
|
233
|
-
return (0, react.createElement)(`div`, { className: `dsu-field${t.wide === !0 ? ` dsu-fieldWide` : ``}` }, (0, react.createElement)(`div`, { className: `dsu-fieldMeta` }, (0, react.createElement)(h, {
|
|
234
|
-
label: t.label,
|
|
235
|
-
detail: t.detail,
|
|
236
|
-
compact: t.compact,
|
|
237
|
-
controlId: t.controlId
|
|
238
|
-
}), t.hint === void 0 ? null : (0, react.createElement)(`p`, {
|
|
239
|
-
className: `dsu-hint`,
|
|
240
|
-
id: n
|
|
241
|
-
}, t.hint)), (0, react.createElement)(`div`, { className: `dsu-control` }, t.children));
|
|
242
|
-
}
|
|
243
|
-
function _(t) {
|
|
244
|
-
let n = (0, react.useId)(), i = t.hint === void 0 ? void 0 : `${n}-hint`;
|
|
245
|
-
return (0, react.createElement)(g, {
|
|
246
|
-
...t,
|
|
247
|
-
controlId: n
|
|
248
|
-
}, (0, react.createElement)(`button`, {
|
|
249
|
-
type: `button`,
|
|
250
|
-
role: `switch`,
|
|
251
|
-
id: n,
|
|
252
|
-
className: `dsu-switch`,
|
|
253
|
-
"aria-checked": t.checked,
|
|
254
|
-
"aria-describedby": i,
|
|
255
|
-
"data-field": t.field,
|
|
256
|
-
disabled: t.disabled === !0,
|
|
257
|
-
onClick: () => {
|
|
258
|
-
t.onChange(!t.checked);
|
|
259
|
-
}
|
|
260
|
-
}, (0, react.createElement)(`span`, { className: `dsu-knob` })));
|
|
261
|
-
}
|
|
262
|
-
function v(t) {
|
|
263
|
-
let i = typeof t.value == `number` && Number.isFinite(t.value) ? String(t.value) : ``, [a, c] = (0, react.useState)(i), [l, u] = (0, react.useState)(!1), d = (0, react.useRef)(void 0), f = (0, react.useId)(), p = (0, react.useId)(), m = [t.hint === void 0 ? void 0 : `${f}-hint`, l ? p : void 0].filter((e) => e !== void 0), h = m.length === 0 ? void 0 : m.join(` `);
|
|
264
|
-
(0, react.useEffect)(() => {
|
|
265
|
-
t.value !== d.current && (d.current = t.value, c(i), u(!1));
|
|
266
|
-
}, [t.value, i]);
|
|
267
|
-
let _ = (e) => {
|
|
268
|
-
let n = t.min ?? -1 / 0, r = t.max ?? 1 / 0;
|
|
269
|
-
if (!Number.isFinite(e) || e < n || e > r) return !1;
|
|
270
|
-
let { step: i } = t;
|
|
271
|
-
if (i === void 0 || i <= 0) return !0;
|
|
272
|
-
let a = (e - (t.min ?? 0)) / i;
|
|
273
|
-
return Math.abs(a - Math.round(a)) < 1e-9;
|
|
274
|
-
}, v = (e) => {
|
|
275
|
-
if (c(e), e === ``) {
|
|
276
|
-
u(!1), d.current = void 0, t.onChange(void 0);
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
let n = Number(e);
|
|
280
|
-
_(n) ? (u(!1), d.current = n, t.onChange(n)) : u(!0);
|
|
281
|
-
}, y = () => {
|
|
282
|
-
a === `` || l || c(String(Number(a)));
|
|
283
|
-
};
|
|
284
|
-
return (0, react.createElement)(g, {
|
|
285
|
-
...t,
|
|
286
|
-
controlId: f
|
|
287
|
-
}, (0, react.createElement)(`div`, { className: `dsu-numberWrap` }, (0, react.createElement)(`div`, { className: `dsu-number` }, (0, react.createElement)(`input`, {
|
|
288
|
-
className: `dsu-input`,
|
|
289
|
-
type: `number`,
|
|
290
|
-
inputMode: `numeric`,
|
|
291
|
-
id: f,
|
|
292
|
-
value: a,
|
|
293
|
-
min: t.min,
|
|
294
|
-
max: t.max,
|
|
295
|
-
step: t.step,
|
|
296
|
-
placeholder: t.placeholder,
|
|
297
|
-
"aria-invalid": l || void 0,
|
|
298
|
-
"aria-describedby": h,
|
|
299
|
-
"data-field": t.field,
|
|
300
|
-
disabled: t.disabled === !0,
|
|
301
|
-
onChange: (e) => {
|
|
302
|
-
v(e.target.value);
|
|
303
|
-
},
|
|
304
|
-
onBlur: y
|
|
305
|
-
}), t.unit === void 0 ? null : (0, react.createElement)(`span`, { className: `dsu-unit` }, t.unit)), l ? (0, react.createElement)(`p`, {
|
|
306
|
-
className: `dsu-error`,
|
|
307
|
-
id: p
|
|
308
|
-
}, t.invalidHint) : null));
|
|
309
|
-
}
|
|
310
|
-
function y(t) {
|
|
311
|
-
let [i, a] = (0, react.useState)(t.value), [o, c] = (0, react.useState)(!1), l = (0, react.useId)(), u = t.hint === void 0 ? void 0 : `${l}-hint`;
|
|
312
|
-
(0, react.useEffect)(() => {
|
|
313
|
-
o || a(t.value);
|
|
314
|
-
}, [t.value, o]);
|
|
315
|
-
let d = {
|
|
316
|
-
id: l,
|
|
317
|
-
className: `dsu-input${t.multiline === !0 ? ` dsu-textarea` : ``}${t.monospace === !0 ? ` dsu-mono` : ``}`,
|
|
318
|
-
value: i,
|
|
319
|
-
placeholder: t.placeholder,
|
|
320
|
-
"aria-describedby": u,
|
|
321
|
-
"data-field": t.field,
|
|
322
|
-
disabled: t.disabled === !0,
|
|
323
|
-
onFocus: () => {
|
|
324
|
-
c(!0);
|
|
325
|
-
},
|
|
326
|
-
onBlur: () => {
|
|
327
|
-
c(!1);
|
|
328
|
-
},
|
|
329
|
-
onChange: (e) => {
|
|
330
|
-
a(e.target.value), t.onChange(e.target.value);
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
return (0, react.createElement)(g, {
|
|
334
|
-
...t,
|
|
335
|
-
controlId: l
|
|
336
|
-
}, t.multiline === !0 ? (0, react.createElement)(`textarea`, d) : (0, react.createElement)(`input`, d));
|
|
337
|
-
}
|
|
338
|
-
function S(t) {
|
|
339
|
-
let n = !t.writable || t.busy || !t.dirty, r;
|
|
340
|
-
return r = t.error === null ? t.writable ? t.dirty ? t.dirtyLabel : t.cleanLabel : t.readOnlyLabel : t.error, (0, react.createElement)(`div`, {
|
|
341
|
-
className: `dsu-savebar`,
|
|
342
|
-
"data-dirty": t.dirty
|
|
343
|
-
}, (0, react.createElement)(`button`, {
|
|
344
|
-
type: `button`,
|
|
345
|
-
className: `dsu-btn dsu-btnPrimary`,
|
|
346
|
-
"data-field": `save`,
|
|
347
|
-
disabled: n,
|
|
348
|
-
onClick: t.onSave
|
|
349
|
-
}, t.busy ? t.savingLabel : t.saveLabel), (0, react.createElement)(`button`, {
|
|
350
|
-
type: `button`,
|
|
351
|
-
className: `dsu-btn`,
|
|
352
|
-
"data-field": `discard`,
|
|
353
|
-
disabled: n,
|
|
354
|
-
onClick: t.onDiscard
|
|
355
|
-
}, t.revertLabel), (0, react.createElement)(`span`, { className: t.error === null ? `dsu-status` : `dsu-status dsu-statusError` }, r));
|
|
356
|
-
}
|
|
11
|
+
let _jayyuen66_dsh_plugin_shared_client = require("@jayyuen66/dsh-plugin-shared/client");
|
|
357
12
|
// src/ui-messages.ts
|
|
358
13
|
const UI_MESSAGES = {
|
|
359
14
|
zh: {
|
|
@@ -614,9 +269,9 @@ function buildGateSection(props) {
|
|
|
614
269
|
const budgetSeconds = budgetMs === void 0 ? void 0 : budgetMs / 1e3;
|
|
615
270
|
const budgetMinSeconds = (GATE_BUDGET_MS_BOUNDS.min ?? 0) / 1e3;
|
|
616
271
|
const budgetMaxSeconds = (GATE_BUDGET_MS_BOUNDS.max ?? 0) / 1e3;
|
|
617
|
-
const enabled = (0, react.createElement)(
|
|
272
|
+
const enabled = (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.ToggleField, {
|
|
618
273
|
label: t("enabledLabel"),
|
|
619
|
-
...
|
|
274
|
+
...(0, _jayyuen66_dsh_plugin_shared_client.splitHint)(t("enabledHint")),
|
|
620
275
|
field: "enabled",
|
|
621
276
|
checked: eff("enabled") ?? true,
|
|
622
277
|
disabled: !writable,
|
|
@@ -624,9 +279,9 @@ function buildGateSection(props) {
|
|
|
624
279
|
setField("enabled", next);
|
|
625
280
|
}
|
|
626
281
|
});
|
|
627
|
-
const injects = (0, react.createElement)(
|
|
282
|
+
const injects = (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.NumberField, {
|
|
628
283
|
label: t("injectsLabel"),
|
|
629
|
-
...
|
|
284
|
+
...(0, _jayyuen66_dsh_plugin_shared_client.splitHint)(t("injectsHint")),
|
|
630
285
|
field: "maxInjectsPerTurn",
|
|
631
286
|
value: maxInjects,
|
|
632
287
|
...MAX_INJECTS_BOUNDS,
|
|
@@ -638,9 +293,9 @@ function buildGateSection(props) {
|
|
|
638
293
|
setField("maxInjectsPerTurn", next);
|
|
639
294
|
}
|
|
640
295
|
});
|
|
641
|
-
const budget = (0, react.createElement)(
|
|
296
|
+
const budget = (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.NumberField, {
|
|
642
297
|
label: t("budgetLabel"),
|
|
643
|
-
...
|
|
298
|
+
...(0, _jayyuen66_dsh_plugin_shared_client.splitHint)(t("budgetHint")),
|
|
644
299
|
field: "gateBudgetMs",
|
|
645
300
|
value: budgetSeconds,
|
|
646
301
|
min: budgetMinSeconds,
|
|
@@ -653,16 +308,16 @@ function buildGateSection(props) {
|
|
|
653
308
|
setField("gateBudgetMs", next === void 0 ? void 0 : next * 1e3);
|
|
654
309
|
}
|
|
655
310
|
});
|
|
656
|
-
return (0, react.createElement)(
|
|
311
|
+
return (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.Section, { title: t("gateSection") }, enabled, injects, budget);
|
|
657
312
|
}
|
|
658
313
|
/** 记忆反馈小节:开关 + 网关地址 + 密钥引用。 */
|
|
659
314
|
function buildMemorySection(props) {
|
|
660
315
|
const { t, writable, eff, setField } = props;
|
|
661
316
|
const gatewayUrl = eff("memoryGatewayUrl") ?? "";
|
|
662
317
|
const keyRef = eff("memoryGatewayKeyRef") ?? "";
|
|
663
|
-
const memory = (0, react.createElement)(
|
|
318
|
+
const memory = (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.ToggleField, {
|
|
664
319
|
label: t("memoryLabel"),
|
|
665
|
-
...
|
|
320
|
+
...(0, _jayyuen66_dsh_plugin_shared_client.splitHint)(t("memoryHint")),
|
|
666
321
|
field: "memoryFeedback",
|
|
667
322
|
checked: eff("memoryFeedback") === true,
|
|
668
323
|
disabled: !writable,
|
|
@@ -670,9 +325,9 @@ function buildMemorySection(props) {
|
|
|
670
325
|
setField("memoryFeedback", next);
|
|
671
326
|
}
|
|
672
327
|
});
|
|
673
|
-
const url = (0, react.createElement)(
|
|
328
|
+
const url = (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.TextField, {
|
|
674
329
|
label: t("gatewayUrlLabel"),
|
|
675
|
-
...
|
|
330
|
+
...(0, _jayyuen66_dsh_plugin_shared_client.splitHint)(t("gatewayUrlHint")),
|
|
676
331
|
field: "memoryGatewayUrl",
|
|
677
332
|
value: gatewayUrl,
|
|
678
333
|
placeholder: "http://127.0.0.1:8420",
|
|
@@ -682,9 +337,9 @@ function buildMemorySection(props) {
|
|
|
682
337
|
setField("memoryGatewayUrl", next.trim() === "" ? void 0 : next.trim());
|
|
683
338
|
}
|
|
684
339
|
});
|
|
685
|
-
const keyRefField = (0, react.createElement)(
|
|
340
|
+
const keyRefField = (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.TextField, {
|
|
686
341
|
label: t("keyRefLabel"),
|
|
687
|
-
...
|
|
342
|
+
...(0, _jayyuen66_dsh_plugin_shared_client.splitHint)(t("keyRefHint")),
|
|
688
343
|
field: "memoryGatewayKeyRef",
|
|
689
344
|
value: keyRef,
|
|
690
345
|
placeholder: "TDAI_GATEWAY_KEY",
|
|
@@ -694,7 +349,7 @@ function buildMemorySection(props) {
|
|
|
694
349
|
setField("memoryGatewayKeyRef", next.trim() === "" ? void 0 : next.trim());
|
|
695
350
|
}
|
|
696
351
|
});
|
|
697
|
-
return (0, react.createElement)(
|
|
352
|
+
return (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.Section, { title: t("memorySection") }, memory, url, keyRefField);
|
|
698
353
|
}
|
|
699
354
|
function DgCard(props) {
|
|
700
355
|
const { t } = props;
|
|
@@ -710,7 +365,7 @@ function DgCard(props) {
|
|
|
710
365
|
const parsedTouched = CARD_FIELD_SCHEMAS[field].safeParse(touched[field]);
|
|
711
366
|
return parsedTouched.success ? parsedTouched.data : void 0;
|
|
712
367
|
};
|
|
713
|
-
const dirtyKeys =
|
|
368
|
+
const dirtyKeys = (0, _jayyuen66_dsh_plugin_shared_client.diffTouched)(touched, value);
|
|
714
369
|
const dirty = dirtyKeys.length > 0;
|
|
715
370
|
const setField = (field, val) => {
|
|
716
371
|
setTouched((prev) => ({
|
|
@@ -719,7 +374,7 @@ function DgCard(props) {
|
|
|
719
374
|
}));
|
|
720
375
|
};
|
|
721
376
|
const save = () => {
|
|
722
|
-
const keys =
|
|
377
|
+
const keys = (0, _jayyuen66_dsh_plugin_shared_client.diffTouched)(touched, value);
|
|
723
378
|
if (keys.length === 0) return;
|
|
724
379
|
setBusy(true);
|
|
725
380
|
setSaveError(null);
|
|
@@ -746,7 +401,7 @@ function DgCard(props) {
|
|
|
746
401
|
};
|
|
747
402
|
const gate = buildGateSection(inputs);
|
|
748
403
|
const memory = buildMemorySection(inputs);
|
|
749
|
-
const saveBar = (0, react.createElement)(
|
|
404
|
+
const saveBar = (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.SaveBar, {
|
|
750
405
|
dirty,
|
|
751
406
|
busy,
|
|
752
407
|
writable,
|
|
@@ -760,7 +415,7 @@ function DgCard(props) {
|
|
|
760
415
|
onSave: save,
|
|
761
416
|
onDiscard: discard
|
|
762
417
|
});
|
|
763
|
-
return (0, react.createElement)(
|
|
418
|
+
return (0, react.createElement)(_jayyuen66_dsh_plugin_shared_client.CardForm, null, gate, memory, saveBar);
|
|
764
419
|
}
|
|
765
420
|
function cardStore(scope) {
|
|
766
421
|
let cachedSnap = null;
|
|
@@ -797,7 +452,7 @@ function apply(ctx) {
|
|
|
797
452
|
ctx.effect(() => {
|
|
798
453
|
const tag = document.createElement("style");
|
|
799
454
|
tag.id = "dsh-card-ui-css-quality-gate";
|
|
800
|
-
tag.textContent =
|
|
455
|
+
tag.textContent = _jayyuen66_dsh_plugin_shared_client.CARD_UI_CSS;
|
|
801
456
|
document.head.append(tag);
|
|
802
457
|
return () => {
|
|
803
458
|
tag.remove();
|
package/cordis.patch.yml
CHANGED
|
@@ -20,5 +20,8 @@
|
|
|
20
20
|
# 行按裸包名引用本包(client-modules 只扫描裸包名条目的 dsh.client 声明)。
|
|
21
21
|
# 注意 pnpm ≥12.3.0:12.0.0 的 add 本地目录有 #14437 bug。
|
|
22
22
|
- insert:
|
|
23
|
+
# 故意不写 id:shared 是本包 patch 启用的内部组件行,不是用户可停用的独立组件;
|
|
24
|
+
# 写 id 会让插件页出现一个可关闭的组件,关闭后本包设置卡会失去组件供应。
|
|
25
|
+
- name: "@jayyuen66/dsh-plugin-shared"
|
|
23
26
|
- id: quality-gate
|
|
24
27
|
name: "@jayyuen66/dsh-quality-gate"
|
package/host.js
CHANGED
|
@@ -7,7 +7,7 @@ import Schema from "@deepseek-ai/schemastery";
|
|
|
7
7
|
import { credentialRef, isCredentialRefName } from "@deepseek-ai/dsh-credentials";
|
|
8
8
|
import { LOCALE_SETTINGS_NAMESPACE, messagesFor, resolveLocalePreference } from "@jayyuen66/dsh-plugin-shared/lib/locale";
|
|
9
9
|
import { deriveProjectKey } from "@jayyuen66/dsh-plugin-shared/lib/project-key";
|
|
10
|
-
import { editPathOf, scanToolEvents, toolEventRowsOf } from "@jayyuen66/dsh-plugin-shared/lib/tool-events";
|
|
10
|
+
import { editPathOf, editPathsOf, scanToolEvents, toolEventRowsOf } from "@jayyuen66/dsh-plugin-shared/lib/tool-events";
|
|
11
11
|
import { settleLessonCall } from "@jayyuen66/dsh-plugin-shared/lib/lesson-bus";
|
|
12
12
|
import { fieldOf, isRecord } from "@jayyuen66/dsh-plugin-shared/lib/record";
|
|
13
13
|
import { z } from "zod";
|
|
@@ -21,7 +21,8 @@ import { z } from "zod";
|
|
|
21
21
|
*/
|
|
22
22
|
const GATED_EXT = /\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs|rs|py|json|ya?ml|toml)$/iu;
|
|
23
23
|
/** 编辑类工具名。str_replace_editor 是 dsh-base 并存的另一套编辑器
|
|
24
|
-
* (host 侧只对 create/str_replace/insert 记账,view
|
|
24
|
+
* (host 侧只对 create/str_replace/insert 记账,view 只读不入这里);apply_patch 是
|
|
25
|
+
* 多文件补丁,路径由 `editPathsOf` 逐枚展开,`note` 仍按单个路径去重记账。
|
|
25
26
|
*
|
|
26
27
|
* 这是**唯一真源**:host 侧的同名判定(isEditTool)也用这一份,两边各写一份时新增
|
|
27
28
|
* 编辑器工具只改一侧就会静默分叉——只改 host 则累加器静默丢弃记账,只改这里则 host
|
|
@@ -29,7 +30,8 @@ const GATED_EXT = /\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs|rs|py|json|ya?ml|toml)$/iu
|
|
|
29
30
|
const EDIT_TOOLS = /* @__PURE__ */ new Set([
|
|
30
31
|
"edit",
|
|
31
32
|
"write",
|
|
32
|
-
"str_replace_editor"
|
|
33
|
+
"str_replace_editor",
|
|
34
|
+
"apply_patch"
|
|
33
35
|
]);
|
|
34
36
|
const DEFAULT_MAX_FILES = 500;
|
|
35
37
|
/** 回合内编辑文件累积器:note O(1),drain 一次性取出并清空。 */
|
|
@@ -449,15 +451,15 @@ const entrySchema = z.object({
|
|
|
449
451
|
failed: z.boolean()
|
|
450
452
|
});
|
|
451
453
|
const editLedgerSchema = z.object({ edits: z.array(entrySchema) });
|
|
452
|
-
/**
|
|
454
|
+
/** 同 callId 的失败回填。普通编辑器沿用“只否定最近一条”的既有语义;`apply_patch`
|
|
455
|
+
* 一条调用可产生多行,只否定最后一行会让其余路径在失败后仍被当成成功编辑,故全部否定。 */
|
|
453
456
|
function failEntry(state, callId) {
|
|
454
|
-
|
|
455
|
-
for (let at =
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
return state.map((entry, at) => at === index ? {
|
|
457
|
+
const matches = [];
|
|
458
|
+
for (let at = 0; at < state.length; at += 1) if (state[at]?.callId === callId) matches.push(at);
|
|
459
|
+
if (matches.length === 0) return state;
|
|
460
|
+
const selected = matches.some((at) => state[at]?.name === "apply_patch") ? matches : matches.slice(-1);
|
|
461
|
+
if (selected.every((at) => state[at]?.failed === true)) return state;
|
|
462
|
+
return state.map((entry, at) => selected.includes(at) ? {
|
|
461
463
|
...entry,
|
|
462
464
|
failed: true
|
|
463
465
|
} : entry);
|
|
@@ -471,14 +473,20 @@ const editLedgerProjection = {
|
|
|
471
473
|
const { call, result } = toolEventRowsOf(event, event.seq);
|
|
472
474
|
let { edits } = state;
|
|
473
475
|
if (call !== void 0 && EDIT_TOOLS.has(call.name) && !call.badArguments) {
|
|
474
|
-
|
|
475
|
-
if (
|
|
476
|
+
let targets;
|
|
477
|
+
if (call.name === "apply_patch") targets = editPathsOf(call);
|
|
478
|
+
else {
|
|
479
|
+
const target = editPathOf(call);
|
|
480
|
+
targets = target.kind === "write" ? [target.path ?? ""] : [];
|
|
481
|
+
}
|
|
482
|
+
const additions = targets.map((filePath) => ({
|
|
476
483
|
seq: call.seq,
|
|
477
484
|
callId: call.callId ?? "",
|
|
478
485
|
name: call.name,
|
|
479
|
-
path:
|
|
486
|
+
path: filePath,
|
|
480
487
|
failed: false
|
|
481
|
-
}
|
|
488
|
+
}));
|
|
489
|
+
edits = [...edits, ...additions];
|
|
482
490
|
}
|
|
483
491
|
if (result !== void 0 && result.isError && result.callId !== void 0 && result.callId !== "") edits = failEntry(edits, result.callId);
|
|
484
492
|
const bounded = edits.length > 1024 ? edits.slice(edits.length - EDIT_LEDGER_MAX) : edits;
|
|
@@ -992,11 +1000,6 @@ async function resolveEventWindow(query, sessionId, lastSeq) {
|
|
|
992
1000
|
}
|
|
993
1001
|
return window;
|
|
994
1002
|
}
|
|
995
|
-
/** 按有无 callId 分流:无 callId 直记(异常/旧形状/测试 mock 保守直记防漏),有则按 callId 关联。 */
|
|
996
|
-
function storeEdit(callId, record, pending, noCallId) {
|
|
997
|
-
if (callId === void 0) noCallId.push(record);
|
|
998
|
-
else pending.set(callId, record);
|
|
999
|
-
}
|
|
1000
1003
|
/**
|
|
1001
1004
|
* 单扫描事件流 → 编辑记账三表:callId→编辑(成功关联)、无 callId 直记表、失败 callId 集。
|
|
1002
1005
|
* 只记成功编辑:被 danger-guard 拒绝或执行失败的 tool/result 标记失败 callId,跳过。
|
|
@@ -1008,22 +1011,25 @@ function storeEdit(callId, record, pending, noCallId) {
|
|
|
1008
1011
|
* - str_replace_editor view(只读)→ 跳过(原 EDIT_SKIP);
|
|
1009
1012
|
* - 其余编辑调用按 callId 关联,tool/result isError 回填 failed。
|
|
1010
1013
|
*/
|
|
1014
|
+
/** 一次编辑调用的全部记账路径。apply_patch 逐操作(含 move 目的)展开;普通编辑器只
|
|
1015
|
+
* 有写动词成行,view 是只读证据;写但没给路径时保留一条 undefined(历史语义)。 */
|
|
1016
|
+
function editPathsFor(call) {
|
|
1017
|
+
if (call.name === "apply_patch") return editPathsOf(call);
|
|
1018
|
+
const target = editPathOf(call);
|
|
1019
|
+
return target.kind === "write" ? [target.path] : [];
|
|
1020
|
+
}
|
|
1011
1021
|
function collectEdits(events) {
|
|
1012
|
-
const
|
|
1013
|
-
const noCallId = [];
|
|
1022
|
+
const records = [];
|
|
1014
1023
|
const failed = /* @__PURE__ */ new Set();
|
|
1015
1024
|
const { calls, results } = scanToolEvents(events);
|
|
1016
|
-
for (const call of calls) if (isEditTool(call.name) && !call.badArguments) {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
}, pending, noCallId);
|
|
1022
|
-
}
|
|
1025
|
+
for (const call of calls) if (isEditTool(call.name) && !call.badArguments) for (const filePath of editPathsFor(call)) records.push({
|
|
1026
|
+
callId: call.callId,
|
|
1027
|
+
name: call.name,
|
|
1028
|
+
path: filePath
|
|
1029
|
+
});
|
|
1023
1030
|
for (const result of results) if (result.isError && result.callId !== void 0) failed.add(result.callId);
|
|
1024
1031
|
return {
|
|
1025
|
-
|
|
1026
|
-
noCallId,
|
|
1032
|
+
records,
|
|
1027
1033
|
failed
|
|
1028
1034
|
};
|
|
1029
1035
|
}
|
|
@@ -1423,8 +1429,7 @@ async function readEditedFiles(rt, agent, acc) {
|
|
|
1423
1429
|
rt.lastSeqBySession.set(sessionId, window.from + window.events.length);
|
|
1424
1430
|
pruneToSessionBudget(rt.lastSeqBySession, sessionId);
|
|
1425
1431
|
const edits = collectEdits(window.events);
|
|
1426
|
-
for (const entry of edits.
|
|
1427
|
-
for (const [callId, entry] of edits.pending) if (!edits.failed.has(callId)) note(entry.name, entry.path);
|
|
1432
|
+
for (const entry of edits.records) if (entry.callId === void 0 || !edits.failed.has(entry.callId)) note(entry.name, entry.path);
|
|
1428
1433
|
}
|
|
1429
1434
|
/**
|
|
1430
1435
|
* 处理一次门禁问题(代码失败或没跑成):上报 + 二次校验 + 配额 + 注入;返回是否停止剩余门禁。
|
|
@@ -1658,17 +1663,15 @@ function apply(ctx, config) {
|
|
|
1658
1663
|
editLedger: { value: void 0 },
|
|
1659
1664
|
disposed: false
|
|
1660
1665
|
};
|
|
1661
|
-
svc.inject(["sessionProjections"], (child) => {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
});
|
|
1671
|
-
});
|
|
1666
|
+
svc.inject(["sessionProjections"], (child) => mountEditLedgerReader(child.sessionProjections, {
|
|
1667
|
+
publish: (reader) => {
|
|
1668
|
+
rt.editLedger.value = reader;
|
|
1669
|
+
},
|
|
1670
|
+
effect: (factory) => child.effect(factory),
|
|
1671
|
+
onError: (error) => {
|
|
1672
|
+
log.warn(`[quality-gate] sessionProjections register failed, falling back to full scan: ${String(error)}`);
|
|
1673
|
+
}
|
|
1674
|
+
}));
|
|
1672
1675
|
svc.on("agent/turn-stopping", async (payload) => {
|
|
1673
1676
|
try {
|
|
1674
1677
|
await handleTurnGate(rt, payload);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jayyuen66/dsh-quality-gate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "dsh 插件:回合收口质量门禁(在 agent/turn-stopping 按项目形态跑 pnpm check / cargo check / ruff check / tsc,未过则注入修复指令开新轮)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ci",
|
|
@@ -46,62 +46,62 @@
|
|
|
46
46
|
"registry": "https://registry.npmjs.org"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@jayyuen66/dsh-plugin-shared": "0.2.
|
|
49
|
+
"@jayyuen66/dsh-plugin-shared": "^0.2.4",
|
|
50
50
|
"typescript": "7.0.2",
|
|
51
51
|
"zod": "4.6.7"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@deepseek-ai/cordis": "4.0
|
|
55
|
-
"@deepseek-ai/cordis-plugin-include": "1.0
|
|
56
|
-
"@deepseek-ai/cordis-plugin-loader": "1.0
|
|
57
|
-
"@deepseek-ai/dsh-agent": "0.2.2-
|
|
58
|
-
"@deepseek-ai/dsh-api-remotes": "0.2.2-
|
|
59
|
-
"@deepseek-ai/dsh-bash-local": "0.2.2-
|
|
60
|
-
"@deepseek-ai/dsh-client-locale": "0.2.2-
|
|
61
|
-
"@deepseek-ai/dsh-client-store": "0.2.2-
|
|
62
|
-
"@deepseek-ai/dsh-client-ui-layout": "0.2.2-
|
|
63
|
-
"@deepseek-ai/dsh-client-ui-plugin-manager": "0.2.2-
|
|
64
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.2.2-
|
|
65
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.2.2-
|
|
66
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.2.2-
|
|
67
|
-
"@deepseek-ai/dsh-credentials": "0.2.2-
|
|
68
|
-
"@deepseek-ai/dsh-http-proxy": "0.2.2-
|
|
69
|
-
"@deepseek-ai/dsh-llm": "0.2.2-
|
|
70
|
-
"@deepseek-ai/dsh-package-manifest": "0.2.2-
|
|
71
|
-
"@deepseek-ai/dsh-sandbox": "0.2.2-
|
|
72
|
-
"@deepseek-ai/dsh-sandbox-policy": "0.2.2-
|
|
73
|
-
"@deepseek-ai/dsh-scope": "0.2.2-
|
|
74
|
-
"@deepseek-ai/dsh-session": "0.2.2-
|
|
75
|
-
"@deepseek-ai/dsh-session-projection": "0.2.2-
|
|
76
|
-
"@deepseek-ai/dsh-session-query": "0.2.2-
|
|
77
|
-
"@deepseek-ai/dsh-session-title": "0.2.2-
|
|
78
|
-
"@deepseek-ai/dsh-settings": "0.2.2-
|
|
79
|
-
"@deepseek-ai/dsh-shell": "0.2.2-
|
|
80
|
-
"@deepseek-ai/dsh-subprocess": "0.2.2-
|
|
81
|
-
"@deepseek-ai/dsh-subprocess-local": "0.2.2-
|
|
82
|
-
"@deepseek-ai/dsh-timeout": "0.2.2-
|
|
83
|
-
"@deepseek-ai/schemastery": "3.
|
|
54
|
+
"@deepseek-ai/cordis": "4.1.0",
|
|
55
|
+
"@deepseek-ai/cordis-plugin-include": "1.1.0",
|
|
56
|
+
"@deepseek-ai/cordis-plugin-loader": "1.1.0",
|
|
57
|
+
"@deepseek-ai/dsh-agent": "0.2.2-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-api-remotes": "0.2.2-rc.1",
|
|
59
|
+
"@deepseek-ai/dsh-bash-local": "0.2.2-rc.1",
|
|
60
|
+
"@deepseek-ai/dsh-client-locale": "0.2.2-rc.1",
|
|
61
|
+
"@deepseek-ai/dsh-client-store": "0.2.2-rc.1",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-layout": "0.2.2-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager": "0.2.2-rc.1",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.2-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.2-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.2-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-credentials": "0.2.2-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-http-proxy": "0.2.2-rc.1",
|
|
69
|
+
"@deepseek-ai/dsh-llm": "0.2.2-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-package-manifest": "0.2.2-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-sandbox": "0.2.2-rc.1",
|
|
72
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.2-rc.1",
|
|
73
|
+
"@deepseek-ai/dsh-scope": "0.2.2-rc.1",
|
|
74
|
+
"@deepseek-ai/dsh-session": "0.2.2-rc.1",
|
|
75
|
+
"@deepseek-ai/dsh-session-projection": "0.2.2-rc.1",
|
|
76
|
+
"@deepseek-ai/dsh-session-query": "0.2.2-rc.1",
|
|
77
|
+
"@deepseek-ai/dsh-session-title": "0.2.2-rc.1",
|
|
78
|
+
"@deepseek-ai/dsh-settings": "0.2.2-rc.1",
|
|
79
|
+
"@deepseek-ai/dsh-shell": "0.2.2-rc.1",
|
|
80
|
+
"@deepseek-ai/dsh-subprocess": "0.2.2-rc.1",
|
|
81
|
+
"@deepseek-ai/dsh-subprocess-local": "0.2.2-rc.1",
|
|
82
|
+
"@deepseek-ai/dsh-timeout": "0.2.2-rc.1",
|
|
83
|
+
"@deepseek-ai/schemastery": "3.19.0",
|
|
84
84
|
"@e18e/eslint-plugin": "0.8.1",
|
|
85
85
|
"@types/node": "26.6.6",
|
|
86
86
|
"@types/react": "19.3.0",
|
|
87
|
-
"@vitest/coverage-v8": "5.0
|
|
88
|
-
"eslint-plugin-regexp": "3.3.
|
|
87
|
+
"@vitest/coverage-v8": "5.1.0",
|
|
88
|
+
"eslint-plugin-regexp": "3.3.3",
|
|
89
89
|
"eslint-plugin-sonarjs": "4.2.2",
|
|
90
90
|
"eslint-plugin-zod": "5.2.2",
|
|
91
91
|
"fast-check": "4.10.2",
|
|
92
|
-
"oxfmt": "0.
|
|
93
|
-
"oxlint": "1.
|
|
92
|
+
"oxfmt": "0.76.0",
|
|
93
|
+
"oxlint": "1.91.0",
|
|
94
94
|
"oxlint-tsgolint": "7.0.2005",
|
|
95
95
|
"react": "^19.3.0",
|
|
96
|
-
"rolldown": "1.2.
|
|
96
|
+
"rolldown": "1.2.16",
|
|
97
97
|
"typescript": "7.0.2",
|
|
98
|
-
"vite": "8.3.
|
|
99
|
-
"vitest": "5.0
|
|
98
|
+
"vite": "8.3.6",
|
|
99
|
+
"vitest": "5.1.0"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
|
-
"@deepseek-ai/dsh": "^0.2.1-rc.3",
|
|
103
|
-
"@deepseek-ai/dsh-credentials": "^0.2.1-rc.3",
|
|
104
|
-
"@deepseek-ai/schemastery": "^3.18.6-alpha.1"
|
|
102
|
+
"@deepseek-ai/dsh": "^0.2.1-rc.5 || ^0.2.2-alpha.3",
|
|
103
|
+
"@deepseek-ai/dsh-credentials": "^0.2.1-rc.5 || ^0.2.2-alpha.3",
|
|
104
|
+
"@deepseek-ai/schemastery": "^3.18.5 || ^3.18.6-alpha.1"
|
|
105
105
|
},
|
|
106
106
|
"peerDependenciesMeta": {
|
|
107
107
|
"@deepseek-ai/dsh": {
|
|
@@ -110,8 +110,8 @@
|
|
|
110
110
|
},
|
|
111
111
|
"icon": "icon.svg",
|
|
112
112
|
"engines": {
|
|
113
|
-
"dsh": "^0.2.1-rc.3",
|
|
114
|
-
"node": ">=
|
|
113
|
+
"dsh": "^0.2.1-rc.5 || ^0.2.2-alpha.3",
|
|
114
|
+
"node": ">=24.9.0"
|
|
115
115
|
},
|
|
116
116
|
"dsh": {
|
|
117
117
|
"manifestVersion": 1,
|
|
@@ -120,7 +120,10 @@
|
|
|
120
120
|
},
|
|
121
121
|
"client": {
|
|
122
122
|
"platform": "web",
|
|
123
|
-
"immediately": true
|
|
123
|
+
"immediately": true,
|
|
124
|
+
"external": [
|
|
125
|
+
"@jayyuen66/dsh-plugin-shared/client"
|
|
126
|
+
]
|
|
124
127
|
}
|
|
125
128
|
},
|
|
126
129
|
"scripts": {
|