mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
Merge pull request #2571 from deepseek-harness/worktree-reducepkg
fix(client): declare browser-only externals as devDependencies
This commit is contained in:
+2
-2
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-30-generated-third-party-notices.md
|
||||
2026-07-30-generated-third-party-notices.md: 9ca24c9634b0410f9b7cc255902344cd6014bb90
|
||||
2026-07-30-generated-third-party-notices.zh.md: b49e60565bbae62cf17026db17babc1ba7a408dc
|
||||
2026-07-30-generated-third-party-notices.md: 2128537b09f9d5d68cb1175b1ecb195fa50e1865
|
||||
2026-07-30-generated-third-party-notices.zh.md: 906f5ed5b9a11f3ddd68491686b460c5d9f4a682
|
||||
|
||||
@@ -26,6 +26,8 @@ The runtime tier deliberately covers **every mountable plugin**, not just what t
|
||||
|
||||
The manifest set is derived from the `packages:` members the root `pnpm-workspace.yaml` declares, including the Landlock workspace and its public packages, so a new member area is read the day it is declared rather than the day someone remembers to extend a list. License and repository metadata come from the root workspace's installed pnpm store and package-local link farms, so the generator requires an installed tree and fails loud when a package resolves to neither, rather than emitting an empty cell. `OVERRIDES` carries the packages whose published manifest cannot answer — Rust-built npm bins that omit `license`, and the `modelcontextprotocol/servers` packages whose repository is mid MIT→Apache-2.0 relicensing, so their effective terms are per-contribution. A runtime dependency whose license is not on the permissive list is a hard error: shipping copyleft is a distribution decision, not something a regenerated table may absorb silently. Vendored packages are cross-checked against `vendor/README.md` and rejected if any is not MIT, and `pnpm-workspace.yaml`'s `patchedDependencies` are listed under the runtime table because pnpm applies those patches at install time — shipped artifacts carry modified copies of `@earendil-works/pi-tui` and `node-pty`, and the patch files are the record of what changed.
|
||||
|
||||
**A package a published artifact carries is runtime whatever section declares it.** The browser side is built, not resolved: tsdown inlines every non-platform specifier into a plugin's `lib/client.js`, and Vite inlines the shell's imports into `@deepseek-ai/dsh-web-frontend`'s `dist`. Those packages are therefore declared as `devDependencies` — [the client build-time dependency note](../../proposed/process/2026-08-14-client-build-time-deps.md) owns that placement rule — while a copy of each is redistributed, so react, shiki, katex, and the markdown pipeline must stay in the runtime tier. [`scripts/browser-bundled-externals.ts`](../../../../scripts/browser-bundled-externals.ts) answers which ones those are from the build itself: it drives each client bundle through that package's own `tsdown.config.ts` and the shell through `apps/web`'s Vite config, with a recording plugin that resolves every bare specifier as external and notes it. The pass walks this repository's own source and stops at the package boundary, which costs about three seconds and yields exactly the direct-dependency granularity this file discloses. A specifier counts only once the host resolves it to a file inside a package, so a bundler's own virtual module is not mistaken for a shipped one: `vite/modulepreload-polyfill` is generated by a Vite plugin rather than published as a file, which makes the polyfill in the `dist` build glue of the same kind as an emitted TypeScript helper. Using the real configurations rather than a hand-kept list buys two properties no list has: an erased type import never appears, because the transform drops it before resolution, and a package stops being disclosed as shipped the moment its last browser import is gone. Workspace names are followed only on the Vite side, where the shell's aliases map them to source — that is how a browser-only library's own imports, `ui-primitives`' katex and shiki among them, become visible. A package that ships only type declarations is development-only however a shipped package names it: it contributes no redistributed code, and the dry run cannot see an erased type import anyway, because the transform drops it before resolution. `@types/mdast` and `micromark-util-types` moved to that tier for exactly this reason.
|
||||
|
||||
The project owner separately authorizes distribution of every official `@anthropic-ai/claude-agent-sdk` version and the official Claude Code CLI/platform payloads that version declares through `optionalDependencies`. The generator represents this as one exact direct-package identity exception, not as a permissive-license override: `SEE LICENSE IN README.md` and `SEE LICENSE IN LICENSE.md` remain non-permissive classifications, and every unrelated non-permissive runtime still fails closed. When the SDK is present, the generator reads its installed manifest, rejects optional identities outside the official SDK payload prefix, derives the current SDK, CLI, and payload versions, verifies the installed host payload's identity, version, and declared-license field, and renders the complete SDK-declared payload set in a separate notices section. Version, declared-license, and payload-set changes do not require new identity authorization, but they still require ordinary dependency, lockfile, compatibility, terms, and notices review.
|
||||
|
||||
## Testing
|
||||
@@ -52,7 +54,7 @@ The Claude distribution tests prove that only the exact direct SDK identity bypa
|
||||
|
||||
## Consequences
|
||||
|
||||
A dependency edit now carries a regenerated notices file into the same commit. Contributors pay one generator run — about a second — on commits that touch a manifest, and nothing on any other commit. Committing with hooks disabled defers the cost to a test-lane failure that names the command.
|
||||
A dependency edit now carries a regenerated notices file into the same commit. Contributors pay one generator run on commits that touch a manifest, and nothing on any other commit. It costs a few seconds rather than one, because learning what the browser artifacts carry drives the real client and shell bundlers; a broken client source therefore fails the hook, as the staged lint job already would. Committing with hooks disabled defers the cost to a test-lane failure that names the command.
|
||||
|
||||
The generator needs an installed tree, which makes it heavier than a pure-source generator, and a new package with unusable published metadata needs an `OVERRIDES` entry rather than silently rendering a blank license. Both failures are loud and name the remedy.
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ Status: implemented
|
||||
|
||||
manifest 集合由根 `pnpm-workspace.yaml` 声明的 `packages:` 成员派生,其中包括 Landlock 工作区及其公开包,因此新增成员区域在声明当天就会被读取,而不必等谁想起来去补一份列表。许可证与仓库地址取自根工作区已安装的 pnpm store 和包本地链接场;某个包两处都解析不到时直接失败,而不是留下空单元格。`OVERRIDES` 收录已发布 manifest 答不上来的包:用 Rust 构建、发布时省略 `license` 字段的 npm 可执行包,以及 `modelcontextprotocol/servers` 系列——该仓库正处在 MIT 向 Apache-2.0 的重新许可过程中,实际条款按贡献逐条而定。运行时依赖的许可证若不在宽松清单内即为硬失败:交付 copyleft 是一项分发决策,不该被一次重新生成悄悄吸收。被源码收编的包会与 `vendor/README.md` 交叉核对,出现非 MIT 即报错;`pnpm-workspace.yaml` 的 `patchedDependencies` 列入运行时表格,因为 pnpm 在安装期就会打上这些补丁——交付产物携带的是改动过的 `@earendil-works/pi-tui` 与 `node-pty`,补丁文件本身就是改动的完整记录。
|
||||
|
||||
**被已发布产物带上的包,无论由哪个区段声明都算 runtime。** 浏览器侧是构建出来的,不是解析出来的:tsdown 把每个非平台 specifier 内联进插件的 `lib/client.js`,Vite 把 shell 的 import 内联进 `@deepseek-ai/dsh-web-frontend` 的 `dist`。因此这些包声明在 `devDependencies`——落位规则归 [客户端构建期依赖那篇](../../proposed/process/2026-08-14-client-build-time-deps.md)——但它们各自都有一份副本被分发出去,所以 react、shiki、katex 与整条 markdown 管线必须留在 runtime 档。到底是哪些包,由 [`scripts/browser-bundled-externals.ts`](../../../../scripts/browser-bundled-externals.ts) 从构建本身取答案:它用各包自己的 `tsdown.config.ts` 驱动每个 client bundle,用 `apps/web` 的 Vite 配置驱动 shell,挂一个记录用插件把每个 bare specifier 解析成 external 并记下来。这一趟只走本仓自己的源码、到包边界即停,约三秒,且给出的正是本文件披露的「直接依赖」粒度。一个 specifier 只有在宿主把它解析到某个包内的文件之后才被计入,所以打包器自己的虚拟模块不会被误当成随产物分发的包:`vite/modulepreload-polyfill` 由 Vite 插件生成而非作为文件发布,因此 `dist` 里那段 polyfill 与 TypeScript 生成的辅助代码同类,属于构建胶水。用真配置而不是一张人工名单,白拿两个手写名单没有的性质:被擦除的类型 import 永远不会出现,因为 transform 在解析前就删了它;某个包最后一处浏览器 import 消失时,它也立刻不再被披露成随产物分发。workspace 名字只在 Vite 那侧继续走,因为 shell 的 alias 会把它们映射到源码——浏览器库包自己的 import,比如 `ui-primitives` 的 katex 与 shiki,正是这样才可见的。 只发布类型声明的包一律算 development-only,无论哪个已发布包具名了它:它不贡献任何被分发的代码,而 dry-run 本来也看不见被擦除的类型 import——transform 在解析前就删了它。`@types/mdast` 与 `micromark-util-types` 正是因此落到该档。
|
||||
|
||||
项目所有者另行授权分发每个官方 `@anthropic-ai/claude-agent-sdk` 版本,以及该版本通过 `optionalDependencies` 声明的官方 Claude Code CLI 与平台载荷。生成器将其表示为一项精确匹配直接包身份的例外,而非宽松许可证覆盖项:`SEE LICENSE IN README.md` 与 `SEE LICENSE IN LICENSE.md` 仍归类为非宽松,所有无关的非宽松运行时依赖仍以默认拒绝方式失败。存在该 SDK 时,生成器会读取其已安装 manifest,拒绝不符合官方 SDK 载荷前缀的可选包身份,推导当前 SDK、CLI 与载荷版本,核验已安装宿主载荷的身份、版本和声明许可证字段,并在单独的声明章节中渲染 SDK 声明的完整载荷集合。版本、声明许可证和载荷集合发生变化时无需新的身份授权,但仍须经过常规的依赖、锁文件、兼容性、条款和声明评审。
|
||||
|
||||
## 测试
|
||||
@@ -52,7 +54,7 @@ Claude 分发测试证明:只有精确匹配的直接 SDK 身份会绕过通
|
||||
|
||||
## 后果
|
||||
|
||||
此后改动依赖时,重新生成的披露文件会随同一个提交入库。触及 manifest 的提交多付一次生成器运行——约一秒;其余提交不受影响。若禁用钩子提交,代价推迟为一次测试 lane 失败,其报错会指明补救命令。
|
||||
此后改动依赖时,重新生成的披露文件会随同一个提交入库。触及 manifest 的提交多付一次生成器运行;其余提交不受影响。这次运行从约一秒变成几秒,因为要弄清浏览器产物带了什么就得驱动真实的 client 与 shell 打包器;因此客户端源码坏掉会让这个 hook 失败,而暂存区的 lint job 本来也会失败。若禁用钩子提交,代价推迟为一次测试 lane 失败,其报错会指明补救命令。
|
||||
|
||||
生成器需要已安装的依赖树,因此比纯源码生成器更重;发布元数据不可用的新包需要补一条 `OVERRIDES`,而不是默默渲染出空白许可证。这两类失败都会明确报错并指出补救方式。
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/proposed/process/2026-08-14-client-build-time-deps.md
|
||||
2026-08-14-client-build-time-deps.md: 0605225a70a16fed9004acfe26adba9b6166f201
|
||||
2026-08-14-client-build-time-deps.zh.md: 5caed3422288711532c40cd767f72c97693e68b4
|
||||
@@ -0,0 +1,100 @@
|
||||
# Agent Note: Client build-time dependencies stay out of the install face
|
||||
|
||||
Status: proposed
|
||||
|
||||
English | [中文](2026-08-14-client-build-time-deps.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
A browser artifact resolves nothing on the user's machine:
|
||||
|
||||
- A `ui-*` plugin package's browser artifact is `lib/client.js`, where tsdown inlines every non-platform specifier (`noExternal` in `packages/client/tsdown.client.ts`). The specifiers that survive are answered by the loader's frozen module table, because `require` inside that bundle is a parameter the loader injects, not Node's.
|
||||
- Platform modules (`PLATFORM_MODULES`) come from the shell `dist`, never from Node resolution.
|
||||
- The shell's own imports are inlined by Vite into `@deepseek-ai/dsh-web-frontend`'s published `dist`; that package ships `dist` alone and has no `.` export.
|
||||
|
||||
Every browser code path is therefore a build product, served as an asset or baked into `dist`. Yet the packages those artifacts are built from — react, react-dom, shiki, katex, clsx, the micromark and mdast families — sit in `dependencies` and non-optional `peerDependencies`, which npm installs for every consumer of the published package. Across the repository that is 79 such external declarations in 38 packages, downloaded by users who never load them.
|
||||
|
||||
## Proposal
|
||||
|
||||
### The rule
|
||||
|
||||
**An external package only a browser artifact reaches belongs in `devDependencies`.** Two deliberate omissions are as much part of the rule:
|
||||
|
||||
- **External packages only.** A `@deepseek-ai/*` name stays where its manifest puts it. Such a declaration also states which package supplies an injected service, which Remote contribution an assembly mounts, or which Loader row must resolve; [verify-runtime-closure](../../../../scripts/verify-runtime-closure.ts) and the Loader read it, and the app installs the package regardless — so moving one removes meaning without removing a download.
|
||||
- **Anything the node half reaches stays**, an erased type import included.
|
||||
|
||||
Faces are walked from the entries a manifest publishes, not by a directory rule, so a module under `src/` that only the browser entry reaches counts as browser source:
|
||||
|
||||
| kind | test | host face entries |
|
||||
| --- | --- | --- |
|
||||
| `bundle-half` | has a `./client` export | every export target except `./client` |
|
||||
| `browser-library` | under `packages/client/` with no `./client` export | `src/invariant.ts` alone — the companion the host mounts; `.` is browser code |
|
||||
| `prebuilt-dist` | no `.` export, ships a `dist` | none: the package offers Node no entry |
|
||||
|
||||
### The gate: `scripts/verify-client-runtime-deps.ts`
|
||||
|
||||
Wired into `pnpm run hygiene`, about 35 seconds — the cost of two bound Programs, the same order as `verify-optional-dependency-imports` in that lane. It reuses the repository's tooling rather than growing its own: `TypeScriptProject` (`scripts/ts-project.ts`) binds the host and client compiler faces separately (that file states why the two cannot share one program — the cordis Context merges collide), `ts.resolveModuleName` resolves relative specifiers, and the walk stops at the package boundary.
|
||||
|
||||
Three findings decided the mechanism, after a first pass that scanned string literals:
|
||||
|
||||
1. A package name must match as a name: the `react` substring inside `'@deepseek-ai/dsh-client-web-react'` silently swallowed react.
|
||||
2. Whether `./client` is the tsdown browser bundle is keyed on the **artifact path** (`./lib/client.js`), not the subpath name — `dsh-goal` publishes `./client` as `./lib/types/client.js`, a plain tsc-emitted browser-shared module.
|
||||
3. `require`, `require.resolve`, and dynamic `import()` on a literal each reach a package; `require.resolve('@deepseek-ai/dsh-web-frontend/dist/index.html')` is a real host resolution path.
|
||||
|
||||
Two classes, both reported per entry:
|
||||
|
||||
| class | count | test |
|
||||
| --- | --- | --- |
|
||||
| `browser` | 74 | only a browser artifact reaches it |
|
||||
| `nothing` | 5 | no reference names it: `client-runtime`'s `react` (which contradicts its own React-free layering red line), the peer `react` of `ui-settings` and `ui-theme`, `ui-trajectory`'s peer `react-dom`, and `ui-primitives`' `@types/mdast` |
|
||||
|
||||
Each conservative rule below answers a false report or a semantic loss observed while building it:
|
||||
|
||||
- **A type reference from the node half keeps its declaration.** `import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'` in `src/invariant.ts` is erased at run time, yet it states which package supplies the service that companion registers — `verify-runtime-closure`'s relation.
|
||||
- **A package publishing a Node entry with no source counterpart is skipped whole, and named in the output.** `dsh-goal`'s `./typert -> ./lib/typert.host.js` is emitted by the typert generator and carries its own `import { z } from 'zod'`, which no source states. Getting this test right removed four false reports, among them `api-gateway`'s `typert-registry`.
|
||||
- A `cordis*.yml` the package owns counts as host face: a Loader row names its plugin instead of importing it.
|
||||
- `@deepseek-ai/cordis` is exempt — check-workspace-constraints requires it as both peer and dev everywhere.
|
||||
|
||||
`--json` output feeds the bulk edit and the install measurement.
|
||||
|
||||
### What leaves an install
|
||||
|
||||
Measured against a real `npm install` of the published CLI, with tarball bytes read from an isolated cache: 103 external tarballs stop being downloaded, 6.05 MB in total.
|
||||
|
||||
| group | packages | saved |
|
||||
| --- | --- | --- |
|
||||
| syntax highlighting and math (shiki family, oniguruma family, katex) | 16 | 3.93 MB |
|
||||
| react and view libraries (react, react-dom, scheduler, immer, zustand, `@tanstack/*`, clsx, use-sync-external-store) | 11 | 1.47 MB |
|
||||
| markdown and ansi pipeline plus odds and ends (micromark, mdast, hast families, anser, a few `@types/*`) | 76 | 0.65 MB |
|
||||
|
||||
Our own six browser-library packages (ui-primitives, ui-slots, web-react, ui-attachment, schema-form, client-web — 0.20 MB together) stay installed: code names them, and the rule above leaves those declarations alone.
|
||||
|
||||
### How it lands, split by nature
|
||||
|
||||
1. **Documentation first**: a declaration section in `packages/client/AGENTS.md`, and one clause in the new-plugin-package checklist.
|
||||
2. **The gate**: `scripts/verify-client-runtime-deps.ts`, its `package.json` script, its place in `hygiene`, and a counterexample spec.
|
||||
3. **The manifests**: 79 entries in 38 packages. 50 need a new `devDependencies` entry; the rest already carry one, so the change is a deleted line.
|
||||
4. **Re-measure after the next release** with the same method, confirming the 103 tarballs stay gone.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Scanning string literals**: the first implementation, rejected by the three findings above — the react-inside-web-react substring had already produced a silent miss.
|
||||
- **Reading built artifacts (`lib/**/*.js`) instead of source**: that is Node's own view, but the gate would then depend on `pnpm run build`, and it still cannot judge a browser-library's `lib/index.js` (node platform, browser content), so the face test stays either way.
|
||||
- **Asking the checker whether a binding is used in a value position** (what `verify-optional-dependency-imports` does): tried, and it also judged 83 node-face type-only declarations movable — no download saved for a real loss of meaning, 53 of them `dsh-invariants`. This gate needs to know whether a reference exists, not whether it is a value.
|
||||
- **Also clearing our own six browser-library packages from the install face**, on the test that no install loads one: another 0.20 MB, at the price of deleting 74 workspace declarations that code genuinely names. Ruled out (2026-08-14): keep what the code names. The cleaner end state is to stop publishing those six packages, which is its own proposal.
|
||||
- **`peerDependenciesMeta.optional` instead of `devDependencies`**: npm does skip an optional peer, but the meaning is "a consumer may supply this", and there is no run-time consumer at all. The repository must install it to build, which is what `devDependencies` says.
|
||||
- **Leaving it to knip**: out of scope for knip, which reports a declared package nothing imports. These specifiers are imported; a bundler inlines them. The evidence is that they persisted on master with knip green. Only the five `nothing` entries overlap.
|
||||
- **`optionalDependencies`**: wrong meaning — it says "skip this if it cannot be installed".
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- `pnpm run hygiene` includes `verify-client-runtime-deps` and passes; a counterexample spec proves one `dependencies.react` is rejected.
|
||||
- `pnpm run build`, `pnpm run test:gui`, and `DSH_SNAPSHOT=replay pnpm run test:web` pass — the move changes no build input, so artifacts stay byte-identical.
|
||||
- A real install after the next release no longer downloads the 103 tarballs above.
|
||||
|
||||
## Risks
|
||||
|
||||
- **The six browser-library packages that stay installed carry bare imports nothing resolves**: `ui-primitives/lib/index.js` is a rolldown artifact and still reads `from "anser"`, while anser is now dev-only. It is inert — only our Vite build reads that file, and no loader exists for it on a user's machine (verified: only browser code imports those packages, never the host). Retiring their publication is the way to erase it; see Alternatives.
|
||||
- **`@types/*` go unreported**: source never names them, so the rule cannot see them. `@types/mdast` was caught only because nothing referenced it either. They belong in dev regardless, and closing that gap is follow-up work.
|
||||
- **A skipped package is unprotected**: `dsh-goal` is skipped whole for its generated entry, so its browser-side declarations are now nobody's business. Reading a generated artifact's own run-time imports is what would let the exemption be withdrawn.
|
||||
- **A false report would delete a declaration something needs at run time**: three defenses hold that line — literal arguments to `require`, `require.resolve`, and dynamic `import()` count as references; a package's own `cordis*.yml` counts as host face; and no `@deepseek-ai/*` name is subject at all.
|
||||
@@ -0,0 +1,100 @@
|
||||
# Agent Note: 客户端构建期依赖不进安装面
|
||||
|
||||
Status: proposed
|
||||
|
||||
[English](2026-08-14-client-build-time-deps.md) | 中文
|
||||
|
||||
## Problem
|
||||
|
||||
浏览器产物不在用户机上解析任何 specifier:
|
||||
|
||||
- `ui-*` 插件包的浏览器产物是 `lib/client.js`,tsdown 把每个非平台 specifier 直接内联(`packages/client/tsdown.client.ts` 的 `noExternal`)。留下来的 specifier 由 loader 的冻结模块表应答——那个 bundle 里的 `require` 是 loader 注入的形参,不是 Node 的。
|
||||
- 平台模块(`PLATFORM_MODULES`)由 shell `dist` 提供,不走 Node 解析。
|
||||
- shell 自身的 import 由 Vite 内联进 `@deepseek-ai/dsh-web-frontend` 已发布的 `dist`;该包只发 `dist`,连 `.` 导出都没有。
|
||||
|
||||
所以浏览器的每条代码路径都是构建产物,或作为静态资源下发,或烤进 `dist`。但这些产物的构建输入——react、react-dom、shiki、katex、clsx、micromark 与 mdast 全族——现在写在 `dependencies` 和非 optional `peerDependencies` 里,而 npm 对每个消费者都会安装这两个区段。全仓 38 个包共 79 处这样的外部依赖声明,装给了永远不会加载它们的用户。
|
||||
|
||||
## Proposal
|
||||
|
||||
### 规则
|
||||
|
||||
**只被浏览器产物触及的外部包落 `devDependencies`。** 两条留白同样是规则的一部分:
|
||||
|
||||
- **只管外部依赖**。`@deepseek-ai/*` 一律留在原处:那些声明还表达「谁提供我注入的服务」「这个 assembly 挂载了谁的 Remote」「哪个 Loader 行必须能解析」,[verify-runtime-closure](../../../../scripts/verify-runtime-closure.ts) 与 Loader 都读它,而 app 无论如何都会装那个包——移走只是删掉语义,并没有减少下载。
|
||||
- **node 面触及的一律不动**,包括被擦除的类型引用。
|
||||
|
||||
face 从 manifest 真正发布的入口走图,不用目录规则,所以 `src/` 下只被浏览器入口触及的模块就算浏览器代码:
|
||||
|
||||
| kind | 判据 | host face 入口 |
|
||||
| --- | --- | --- |
|
||||
| `bundle-half` | 有 `./client` 导出 | 除 `./client` 外的每个导出目标 |
|
||||
| `browser-library` | `packages/client/` 下且无 `./client` 导出 | 只有 `src/invariant.ts`——宿主唯一能挂载的伴生模块;`.` 面是浏览器代码 |
|
||||
| `prebuilt-dist` | 无 `.` 导出、发布 `dist` | 没有:这个包不给 Node 提供任何入口 |
|
||||
|
||||
### 门禁:`scripts/verify-client-runtime-deps.ts`
|
||||
|
||||
接入 `pnpm run hygiene`,约 35 秒——两个绑定 Program 的开销,与同 lane 的 `verify-optional-dependency-imports` 同量级。复用仓内既有工具而不自造一套:`TypeScriptProject`(`scripts/ts-project.ts`)分别绑定 host 与 client 两个编译面(该文件写明两者不能合进一个 program——cordis Context merge 会撞),相对 specifier 交给 `ts.resolveModuleName` 解析,遍历到包边界即停。
|
||||
|
||||
判据要害有三条,都是起手那版扫字符串字面量踩出来的:
|
||||
|
||||
1. 包名必须按名匹配:`'@deepseek-ai/dsh-client-web-react'` 里的 `react` 子串会静默吞掉 react。
|
||||
2. `./client` 是不是 tsdown 浏览器 bundle,看的是**产物路径**(`./lib/client.js`)而不是子路径名——`dsh-goal` 的 `./client` 是 `./lib/types/client.js`,一个 tsc 直出的浏览器共享模块。
|
||||
3. `require`、`require.resolve`、动态 `import()` 的字面量实参都能触及一个包;`require.resolve('@deepseek-ai/dsh-web-frontend/dist/index.html')` 就是真实存在的宿主解析路径。
|
||||
|
||||
两类判定,逐条报告:
|
||||
|
||||
| 类 | 数量 | 判据 |
|
||||
| --- | --- | --- |
|
||||
| `browser` | 74 | 只有浏览器产物触及 |
|
||||
| `nothing` | 5 | 没有任何引用具名它:`client-runtime` 的 `react`(与它自己「零 React 引用」的分层红线矛盾)、`ui-settings` 与 `ui-theme` 的 peer `react`、`ui-trajectory` 的 peer `react-dom`、`ui-primitives` 的 `@types/mdast` |
|
||||
|
||||
下面每条保守规则都对应一次实测到的误报或语义损失:
|
||||
|
||||
- **node 面的类型引用保留声明。** `src/invariant.ts` 里的 `import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'` 运行期被擦除,但它声明了谁提供这个伴生插件要注册的服务——归 `verify-runtime-closure` 管的关系。
|
||||
- **发布了没有源码对应文件的 Node 入口的包整包跳过,并在输出里点名。** `dsh-goal` 的 `./typert -> ./lib/typert.host.js` 由 typert 生成器直出,自带 `import { z } from 'zod'`,没有任何源码陈述这件事。把这条判据修对,消掉了四处误报,其中包括 `api-gateway` 的 `typert-registry`。
|
||||
- 包自带的 `cordis*.yml` 算 host face:Loader 行是具名它的插件而不是 import 它。
|
||||
- `@deepseek-ai/cordis` 豁免——check-workspace-constraints 要求它在每个包里同时是 peer 和 dev。
|
||||
|
||||
`--json` 输出供批量改写与安装体积实测复用。
|
||||
|
||||
### 安装面少掉什么
|
||||
|
||||
对已发布 CLI 真装一遍实测,tarball 字节从独立 cache 读出:103 个外部 tarball 不再下载,合计 6.05 MB。
|
||||
|
||||
| 组 | 包数 | 省 |
|
||||
| --- | --- | --- |
|
||||
| 语法高亮与数学(shiki 族、oniguruma 族、katex) | 16 | 3.93 MB |
|
||||
| react 与视图库(react、react-dom、scheduler、immer、zustand、`@tanstack/*`、clsx、use-sync-external-store) | 11 | 1.47 MB |
|
||||
| markdown 与 ansi 管线及零碎(micromark、mdast、hast 全族、anser、若干 `@types/*`) | 76 | 0.65 MB |
|
||||
|
||||
我们自己的 6 个浏览器库包(ui-primitives、ui-slots、web-react、ui-attachment、schema-form、client-web,合计 0.20 MB)仍留在安装面:代码确实具名它们,上面的规则不动那些声明。
|
||||
|
||||
### 分刀落地
|
||||
|
||||
1. **文档住顶刀**:`packages/client/AGENTS.md` 的依赖声明节,加新插件包 checklist 里的一句。
|
||||
2. **门禁**:`scripts/verify-client-runtime-deps.ts`、它的 `package.json` 脚本、它在 `hygiene` 里的位置,以及一条反例 spec。
|
||||
3. **manifest**:38 个包 79 处。其中 50 处需要新增 `devDependencies` 条目,其余包已有同名条目,改动就是删掉一行。
|
||||
4. **发版后按同一方法复测**,确认这 103 个 tarball 没有回来。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **扫字符串字面量**:起手就是这么实现的,被上面三条要害否掉——react-in-web-react 的子串已经造成过一次静默漏报。
|
||||
- **读构建产物(`lib/**/*.js`)而不是源码**:那是 Node 自己的视角,但门禁从此依赖 `pnpm run build`,而且它照样判不了浏览器库包的 `lib/index.js`(platform 是 node、内容是浏览器代码),face 判据两种走法都得有。
|
||||
- **用 checker 判绑定是否用在值位置**(`verify-optional-dependency-imports` 就是这么做的):试过,它把 83 处 node 面纯类型声明也判成可移出——没省下任何下载,却实打实损失语义,其中 53 处是 `dsh-invariants`。本门禁要知道的是引用是否存在,而不是它是值还是类型。
|
||||
- **顺带把我们自己的 6 个浏览器库包也清出安装面**,判据是任何安装都不加载它们:再省 0.20 MB,代价是删掉 74 处代码确实具名的 workspace 声明。已否(2026-08-14):代码用到的就保留。更干净的终态是这 6 个包不再发布,那是另一个提案。
|
||||
- **用 `peerDependenciesMeta.optional` 而不是 `devDependencies`**:npm 确实会跳过 optional peer,但那个语义是「消费者可以自行提供」,而这里根本没有运行期消费者。仓内必须装一份才能构建,这正是 `devDependencies` 的意思。
|
||||
- **交给 knip**:不属于 knip 的范畴,它报的是「声明了但没人 import」。这些 specifier 确实被 import,只是被打包器内联了。实证就是它们在 master 上长期存在而 knip 全绿。只有 `nothing` 那 5 条与它重叠。
|
||||
- **用 `optionalDependencies`**:语义错,它说的是「装不上就跳过」。
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- `pnpm run hygiene` 包含 `verify-client-runtime-deps` 并通过;一条反例 spec 证明一处 `dependencies.react` 会被拒。
|
||||
- `pnpm run build`、`pnpm run test:gui`、`DSH_SNAPSHOT=replay pnpm run test:web` 通过——这次迁移不改任何构建输入,产物应逐字节等价。
|
||||
- 发版后真装一遍,上面那 103 个 tarball 不再被下载。
|
||||
|
||||
## Risks
|
||||
|
||||
- **留在安装面的 6 个浏览器库包会带着解析不了的 bare import**:`ui-primitives/lib/index.js` 是 rolldown 产物,仍写着 `from "anser"`,而 anser 已经只在 dev。它是惰性的——只有我们的 Vite 构建会读这个文件,用户机上没有任何加载者(已实证:只有浏览器代码 import 它们,宿主从不)。要彻底消掉就让这些包不再发布,见 Alternatives。
|
||||
- **`@types/*` 报不出来**:源码从不具名它们,规则看不见。`@types/mdast` 被抓到只是因为恰好也没有任何引用。它们本来就该在 dev,补这个缺口是后续的事。
|
||||
- **被跳过的包没人管**:`dsh-goal` 因生成入口整包跳过,它浏览器侧的声明现在没有门禁看着。能读到生成产物自身的运行期 import,这条豁免才能收回。
|
||||
- **误报会删掉运行期真需要的声明**:三层兜底守住这条线——`require`、`require.resolve`、动态 `import()` 的字面量实参都算引用;包自带的 `cordis*.yml` 算 host face;`@deepseek-ai/*` 整体不在判据范围内。
|
||||
@@ -27,7 +27,7 @@ The Cordis framework and its foundation libraries are source-vendored into this
|
||||
|
||||
## Runtime npm dependencies
|
||||
|
||||
External packages that a workspace package resolves at runtime. The tier covers every plugin a user can mount from `cordis.yml` — not only what the `dsh` CLI, Web UI, and Python SDK runtime load by default.
|
||||
External packages that reach a user: a workspace package resolves them at runtime, or a published browser artifact carries a copy of their code. The tier covers every plugin a user can mount from `cordis.yml` — not only what the `dsh` CLI, Web UI, and Python SDK runtime load by default — and the packages the client build inlines into a plugin bundle or the shell `dist`, which are declared as `devDependencies` because nothing on a user's machine resolves their specifiers.
|
||||
|
||||
| Package | License |
|
||||
| --- | --- |
|
||||
@@ -48,7 +48,6 @@ External packages that a workspace package resolves at runtime. The tier covers
|
||||
| [`@shikijs/langs`](https://github.com/shikijs/shiki) | MIT |
|
||||
| [`@standard-schema/spec`](https://github.com/standard-schema/standard-schema) | MIT |
|
||||
| [`@tanstack/react-virtual`](https://github.com/TanStack/virtual) | MIT |
|
||||
| [`@types/mdast`](https://github.com/DefinitelyTyped/DefinitelyTyped) | MIT |
|
||||
| [`@vscode/ripgrep`](https://github.com/microsoft/vscode-ripgrep) | MIT |
|
||||
| [`anser`](https://github.com/IonicaBizau/anser) | MIT |
|
||||
| [`chokidar`](https://github.com/paulmillr/chokidar) | MIT |
|
||||
@@ -73,7 +72,6 @@ External packages that a workspace package resolves at runtime. The tier covers
|
||||
| [`micromark-util-classify-character`](https://github.com/micromark/micromark/tree/main/packages/micromark-util-classify-character) | MIT |
|
||||
| [`micromark-util-sanitize-uri`](https://github.com/micromark/micromark/tree/main/packages/micromark-util-sanitize-uri) | MIT |
|
||||
| [`micromark-util-symbol`](https://github.com/micromark/micromark/tree/main/packages/micromark-util-symbol) | MIT |
|
||||
| [`micromark-util-types`](https://github.com/micromark/micromark/tree/main/packages/micromark-util-types) | MIT |
|
||||
| [`node-addon-require-builtin`](https://www.npmjs.com/package/node-addon-require-builtin) | MIT |
|
||||
| [`node-pty`](https://github.com/microsoft/node-pty) | MIT |
|
||||
| [`picomatch`](https://github.com/micromatch/picomatch) | MIT |
|
||||
@@ -115,7 +113,7 @@ The installed SDK 0.3.220 declares the following optional platform packages. Eac
|
||||
|
||||
## Development-only npm dependencies
|
||||
|
||||
External packages **directly declared** only by repository tooling, test infrastructure, the documentation site, the demo leaves, or the native launcher's build workspace. No shipped surface names them itself. A package here may still be pulled in transitively by a runtime dependency — `pnpm-lock.yaml` is the authority on the full closure — so this tier records who declares a package, not what a build ultimately bundles.
|
||||
External packages **directly declared** only by repository tooling, test infrastructure, the documentation site, the demo leaves, or the native launcher's build workspace, and carried by no published artifact. No shipped surface names them itself. A package here may still be pulled in transitively by a runtime dependency — `pnpm-lock.yaml` is the authority on the full closure — so this tier records who declares a package, not what a build ultimately bundles.
|
||||
|
||||
| Package | License |
|
||||
| --- | --- |
|
||||
@@ -129,6 +127,7 @@ External packages **directly declared** only by repository tooling, test infrast
|
||||
| [`@types/babel__code-frame`](https://github.com/DefinitelyTyped/DefinitelyTyped) | MIT |
|
||||
| [`@types/js-yaml`](https://github.com/DefinitelyTyped/DefinitelyTyped) | MIT |
|
||||
| [`@types/jsdom`](https://github.com/DefinitelyTyped/DefinitelyTyped) | MIT |
|
||||
| [`@types/mdast`](https://github.com/DefinitelyTyped/DefinitelyTyped) | MIT |
|
||||
| [`@types/node`](https://github.com/DefinitelyTyped/DefinitelyTyped) | MIT |
|
||||
| [`@types/picomatch`](https://github.com/DefinitelyTyped/DefinitelyTyped) | MIT |
|
||||
| [`@types/react`](https://github.com/DefinitelyTyped/DefinitelyTyped) | MIT |
|
||||
@@ -154,6 +153,7 @@ External packages **directly declared** only by repository tooling, test infrast
|
||||
| [`lefthook`](https://github.com/evilmartians/lefthook) | MIT |
|
||||
| [`lightningcss`](https://github.com/parcel-bundler/lightningcss) | MPL-2.0 |
|
||||
| [`mermaid`](https://github.com/mermaid-js/mermaid) | MIT |
|
||||
| [`micromark-util-types`](https://github.com/micromark/micromark/tree/main/packages/micromark-util-types) | MIT |
|
||||
| [`oxlint`](https://github.com/oxc-project/oxc) | MIT |
|
||||
| [`oxlint-tsgolint`](https://github.com/oxc-project/tsgolint) | MIT |
|
||||
| [`playwright`](https://github.com/microsoft/playwright) | Apache-2.0 |
|
||||
|
||||
@@ -26,9 +26,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-client-web": "workspace:^",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"@deepseek-ai/dsh-client-web": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis-plugin-group": "workspace:^",
|
||||
@@ -46,6 +44,8 @@
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^6.0.0",
|
||||
"vitest": "^4.1.8",
|
||||
"fflate": "^0.8.2"
|
||||
"fflate": "^0.8.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ export default defineConfig({
|
||||
plugins: [rejectStandaloneServe(), react()],
|
||||
build: {
|
||||
sourcemap: true,
|
||||
modulePreload: { polyfill: false },
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// Output layout: the two main chunks stay at assets/ root; lazy
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ pre-commit:
|
||||
# lefthook only inspects files present on disk — so that one case still
|
||||
# falls through to the freshness assertion in the test lane.
|
||||
- name: third-party notices (staged)
|
||||
glob: '{package.json,*/package.json,*/*/package.json,*/*/*/package.json,*/*/*/*/package.json,pnpm-workspace.yaml,*/*/pnpm-workspace.yaml,pnpm-lock.yaml,vendor/README.md,python/*/pyproject.toml,scripts/gen-third-party-notices.ts,scripts/build-exe-for-python-sdk.ts}'
|
||||
glob: '{package.json,*/package.json,*/*/package.json,*/*/*/package.json,*/*/*/*/package.json,pnpm-workspace.yaml,*/*/pnpm-workspace.yaml,pnpm-lock.yaml,vendor/README.md,python/*/pyproject.toml,scripts/gen-third-party-notices.ts,scripts/browser-bundled-externals.ts,packages/client/tsdown.client.ts,scripts/build-exe-for-python-sdk.ts}'
|
||||
run: node_modules/.bin/tsx scripts/gen-third-party-notices.ts && git add THIRD_PARTY_NOTICES.md
|
||||
|
||||
- name: whitespace (staged)
|
||||
|
||||
+2
-1
@@ -102,6 +102,7 @@
|
||||
"rescope-vendor": "tsx scripts/rescope-vendor.ts",
|
||||
"rescope-vendor:check": "tsx scripts/rescope-vendor.ts --check",
|
||||
"verify-client-domain-graph": "tsx scripts/verify-client-domain-graph.ts",
|
||||
"verify-client-runtime-deps": "tsx scripts/verify-client-runtime-deps.ts",
|
||||
"gen-cordis-catalog": "tsx scripts/gen-cordis-catalog.ts",
|
||||
"verify-cordis-catalog": "tsx scripts/gen-cordis-catalog.ts --check",
|
||||
"gen-cordis-api": "tsx scripts/gen-cordis-api.ts",
|
||||
@@ -126,7 +127,7 @@
|
||||
"verify-module-graph": "tsx scripts/gen-module-graph.ts --check",
|
||||
"constraints": "tsx scripts/check-workspace-constraints.ts",
|
||||
"doc-sync": "tsx scripts/run-gates.ts doc-sync",
|
||||
"hygiene": "pnpm run rescope-vendor:check && pnpm run knip && pnpm run publint && pnpm run constraints && pnpm run verify-dsh-package-licenses && pnpm run verify-package-invariants && pnpm run verify-built-package-invariants && pnpm run verify-cordis-config && pnpm run verify-node-next-types && pnpm run verify-optional-dependency-imports && pnpm run verify-runtime-closure && pnpm run verify-vendored-links",
|
||||
"hygiene": "pnpm run rescope-vendor:check && pnpm run knip && pnpm run publint && pnpm run constraints && pnpm run verify-dsh-package-licenses && pnpm run verify-package-invariants && pnpm run verify-built-package-invariants && pnpm run verify-cordis-config && pnpm run verify-node-next-types && pnpm run verify-optional-dependency-imports && pnpm run verify-runtime-closure && pnpm run verify-client-runtime-deps && pnpm run verify-vendored-links",
|
||||
"publish:npm-baseline": "tsx scripts/publish-npm-baseline.ts",
|
||||
"release:dsh": "tsx scripts/release/bump.ts --family dsh",
|
||||
"release:vendor": "tsx scripts/release/bump.ts --family vendor",
|
||||
|
||||
@@ -35,6 +35,15 @@ The `/client` entrypoint of a UI plugin package is its public browser API, not a
|
||||
2. **Same-package tests import internals directly** — relative `../src/client/xxx.ts` from package tests, or the `./src/*` subpath where a spec lives outside the package. Never widen the public API to make a test compile.
|
||||
3. **Cross-package imports of another plugin's symbols are in principle forbidden.** The sanctioned routes are the slot system (register/renderSlot) and ctx services. If neither fits, stop and escalate — do not add an export to unblock yourself.
|
||||
|
||||
## Dependency declaration
|
||||
|
||||
A browser artifact resolves nothing on the user's machine: tsdown inlines every non-platform specifier into `lib/client.js`, the shell `dist` answers `PLATFORM_MODULES` from its frozen module table, and Vite inlines the shell's own imports into the published `dist`.
|
||||
|
||||
- **An external package only browser code reaches belongs in `devDependencies`.** npm installs `dependencies` and non-optional `peerDependencies` for every consumer, so react, shiki, katex, or clsx declared there ships to users who never load it.
|
||||
- **A workspace name stays where it is.** Such a declaration also states which package supplies an injected service or a mounted Remote contribution — [verify-runtime-closure](../../scripts/verify-runtime-closure.ts) and the Loader read it, and the app installs the package regardless.
|
||||
- **The node half decides.** Anything its published entries reach — an erased type import, a `require.resolve`, and a Loader row in the package's own `cordis*.yml` included — stays declared as it is.
|
||||
- `pnpm run verify-client-runtime-deps` (inside `hygiene`) names each offending entry; knip owns whether the surviving declaration is used at all.
|
||||
|
||||
## ctx discipline (components never see ctx)
|
||||
|
||||
`ctx` belongs to the apply world only: the plugin body and the inject factories closed over it. Components — every `.tsx` under a feature domain — receive all data and callbacks **through the four props shares**; they never call a hook that reaches ctx, never import a service class to poke it, never read a React context (business components see zero contexts — `BindingContext` and its kin are renderer-internal). If a component needs something new, the answer is a prop threaded from its share's source (owner site, store declaration, or inject face), not a hook.
|
||||
@@ -91,7 +100,7 @@ If `test:gui` is red on code you did not touch, neither silently fix nor ignore
|
||||
|
||||
Bringing up a new `packages/client/<name>` plugin package (ui-workspace is a complete example; ui-sidebar/ui-user-questions are minimal skeletons):
|
||||
|
||||
1. **Package skeleton**: `package.json` (`@deepseek-ai/dsh-client-<name>`, exports `.`/`./invariant`/`./client`/`./src/*`/`./package.json`, `dsh.client` manifest, `files` list), `tsconfig.json` (extends `tsconfig.base.client.json`, one `references` entry per workspace dependency plus `runtime-diagnostics/invariants`), `tsdown.config.ts` (`clientBundle(id, ['lib/types/index.js', 'lib/types/invariant.js'])`), `src/index.ts` (empty node-half apply), `src/invariant.ts` (companion with a real reason), `src/css-modules.d.ts` when using CSS Modules, `README.md` with the Model Experience section.
|
||||
1. **Package skeleton**: `package.json` (`@deepseek-ai/dsh-client-<name>`, exports `.`/`./invariant`/`./client`/`./src/*`/`./package.json`, `dsh.client` manifest, `files` list, and every browser-side external package under `devDependencies` per the [declaration rules](#dependency-declaration)), `tsconfig.json` (extends `tsconfig.base.client.json`, one `references` entry per workspace dependency plus `runtime-diagnostics/invariants`), `tsdown.config.ts` (`clientBundle(id, ['lib/types/index.js', 'lib/types/invariant.js'])`), `src/index.ts` (empty node-half apply), `src/invariant.ts` (companion with a real reason), `src/css-modules.d.ts` when using CSS Modules, `README.md` with the Model Experience section.
|
||||
2. **Three registration surfaces, all required** (missing any one fails at a different, later point): the `tsconfig.client.json` aggregate `references` entry; a `dsh.client` row in `packages/bundle/web-app/cordis.patch.yml`; a `packages/bundle/web-app/package.json` dependency (profile boots resolve bare row names through the healed `$DSH_HOME/profiles/node_modules` fallback, which mirrors the app's and each bundle's declared dependencies — a row whose package no manifest declares fails to import). `pnpm-workspace.yaml` already globs `packages/*/*`.
|
||||
3. **dsh.client manifest semantics**: `platform: 'web'` always; `immediately: true` only for stage-one-prefetch infrastructure rows. `inject` lists package-name dependency edges — they are **informational only** (preflight display, HMR diffing); they do not sequence entry activation or apply order. Activation order is cordis fiber inject waiting on *services*, nothing else.
|
||||
4. **Registering into another package's slot**: apply order is unconstrained, and a business service is not a declaration barrier. Use `ctx.slots.inject(name, () => ctx.slots.register(...))`; it waits on the actual declaration, removes the contribution when that declaration collapses, reruns after redeclaration, and leaves with the caller's plugin fiber. Return a generator yielding each registration when several contributions must install and roll back atomically. A bare `slots.register` into an undeclared slot remains an error; keep service edges only for services the contribution actually reads.
|
||||
|
||||
@@ -50,8 +50,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
|
||||
@@ -53,10 +53,7 @@
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-projection": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-title": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"immer": "^10.1.1",
|
||||
"react": "^18.2.0",
|
||||
"zustand": "~4.4.7"
|
||||
"@deepseek-ai/dsh-tools": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
@@ -72,7 +69,10 @@
|
||||
"@deepseek-ai/dsh-timeout": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-registry": "workspace:^",
|
||||
"@types/react": "~18.3.1"
|
||||
"@types/react": "~18.3.1",
|
||||
"immer": "^10.1.1",
|
||||
"react": "^18.2.0",
|
||||
"zustand": "~4.4.7"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -58,8 +58,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-web-react": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
|
||||
@@ -28,16 +28,16 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-attachment": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"clsx": "^2.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@types/react-dom": "~18.3.0"
|
||||
"@types/react-dom": "~18.3.0",
|
||||
"clsx": "^2.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -46,9 +46,6 @@
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
@@ -59,8 +56,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-commands": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
@@ -76,7 +72,8 @@
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"react": "^18.2.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-settings": "workspace:^",
|
||||
"clsx": "^2.0.0",
|
||||
"@deepseek-ai/schemastery": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -72,8 +71,7 @@
|
||||
"@deepseek-ai/dsh-llm-retry": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-stats": "workspace:^",
|
||||
"@deepseek-ai/dsh-token-meter": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-tools": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
@@ -104,7 +102,8 @@
|
||||
"@deepseek-ai/dsh-tool-todo": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"react": "^18.2.0"
|
||||
"react": "^18.2.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -45,9 +45,6 @@
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-connection": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
@@ -68,7 +65,8 @@
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-system-prompt": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
@@ -54,8 +51,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-workspace": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
@@ -69,7 +65,8 @@
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -48,8 +48,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-workspace": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
|
||||
@@ -55,8 +55,7 @@
|
||||
"@deepseek-ai/dsh-commands": "workspace:^",
|
||||
"@deepseek-ai/dsh-goal": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
|
||||
@@ -43,17 +43,13 @@
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
@@ -64,7 +60,8 @@
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"react": "^18.2.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -45,9 +45,6 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
@@ -66,7 +63,8 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -48,8 +48,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-theme": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
|
||||
@@ -56,8 +56,7 @@
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-message-feedback": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
|
||||
@@ -56,9 +56,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"clsx": "^2.1.1",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
|
||||
@@ -60,8 +60,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-permission-presets": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-permission-presets": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
|
||||
@@ -53,8 +53,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-plan-mode": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
|
||||
@@ -26,7 +26,11 @@
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@types/react-dom": "~18.3.0",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"@shikijs/langs": "^4.3.1",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"anser": "^2.3.5",
|
||||
@@ -48,12 +52,6 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"shiki": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@types/react-dom": "~18.3.0",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/invariant.js",
|
||||
|
||||
@@ -49,8 +49,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-settings": "workspace:^",
|
||||
"@deepseek-ai/schemastery": "workspace:^",
|
||||
"clsx": "^2.0.0"
|
||||
"@deepseek-ai/schemastery": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
@@ -63,8 +62,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-web-react": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
@@ -80,7 +78,8 @@
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"react": "^18.2.0"
|
||||
"react": "^18.2.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -54,8 +54,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-web-react": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
|
||||
@@ -53,8 +53,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
|
||||
@@ -56,8 +56,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-web-react": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
@@ -72,6 +71,7 @@
|
||||
"@deepseek-ai/dsh-client-web-react": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"clsx": "^2.0.0",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
@@ -79,8 +79,5 @@
|
||||
"lib/invariant.js",
|
||||
"lib/client.js",
|
||||
"lib/types/**/*.d.ts"
|
||||
],
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@
|
||||
"@deepseek-ai/dsh-client-schema-form": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-settings": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-settings": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
|
||||
@@ -44,17 +44,13 @@
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
@@ -66,7 +62,8 @@
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"react": "^18.2.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -56,8 +56,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-tool": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
|
||||
@@ -46,9 +46,6 @@
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
@@ -73,7 +70,8 @@
|
||||
"@deepseek-ai/dsh-subagent": "workspace:^",
|
||||
"@deepseek-ai/dsh-token-meter": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -54,8 +54,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-host-webserver": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
@@ -69,6 +68,7 @@
|
||||
"@deepseek-ai/dsh-host-webserver": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"clsx": "^2.0.0",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
@@ -84,7 +84,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-settings": "workspace:^",
|
||||
"clsx": "^2.0.0",
|
||||
"@deepseek-ai/schemastery": "workspace:^"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
@@ -55,8 +52,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
@@ -72,6 +68,7 @@
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@types/react": "~18.3.1",
|
||||
"clsx": "^2.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
||||
@@ -44,10 +44,6 @@
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/react-virtual": "^3.14.9",
|
||||
"diff": "^9.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-agent": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
@@ -56,9 +52,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"@deepseek-ai/dsh-compaction": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"@deepseek-ai/dsh-tools": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-agent": "workspace:^",
|
||||
@@ -75,7 +69,9 @@
|
||||
"@types/react-dom": "~18.3.0",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"@tanstack/react-virtual": "^3.14.9",
|
||||
"diff": "^9.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"clsx": "^2.0.0",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
@@ -67,7 +65,9 @@
|
||||
"@deepseek-ai/dsh-system-prompt": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-questions": "workspace:^",
|
||||
"@types/react": "~18.3.1"
|
||||
"@types/react": "~18.3.1",
|
||||
"clsx": "^2.0.0",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -50,9 +50,6 @@
|
||||
"lib/types/**/*.d.ts"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
@@ -77,6 +74,7 @@
|
||||
"@deepseek-ai/dsh-tool-workflow": "workspace:^",
|
||||
"@deepseek-ai/dsh-workflow": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,17 +45,13 @@
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
@@ -68,7 +64,8 @@
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"react": "^18.2.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -27,9 +27,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"react": "^18.2.0",
|
||||
"use-sync-external-store": "1.2.0"
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
@@ -38,7 +36,9 @@
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0",
|
||||
"use-sync-external-store": "1.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -33,9 +33,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-theme": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-web-react": "workspace:^",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"@deepseek-ai/dsh-client-web-react": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
|
||||
@@ -45,7 +43,9 @@
|
||||
"@types/react": "~18.3.1",
|
||||
"@types/react-dom": "~18.3.0",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"typescript": "^6.0.3"
|
||||
"typescript": "^6.0.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
|
||||
|
||||
@@ -54,8 +54,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-theme": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
|
||||
|
||||
@@ -61,8 +61,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-tool": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:^",
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
"name": "@deepseek-ai/dsh-session-log-export",
|
||||
"description": "Web Session-log export command and shared download dialog",
|
||||
"version": "0.1.0-rc.6",
|
||||
"publishConfig": { "access": "public" },
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
||||
@@ -12,14 +14,31 @@
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
"exports": {
|
||||
".": { "types": "./lib/types/index.d.ts", "default": "./lib/index.js" },
|
||||
"./invariant": { "types": "./lib/types/invariant.d.ts", "default": "./lib/invariant.js" },
|
||||
"./client": { "types": "./lib/types/client/index.d.ts", "default": "./lib/client.js" },
|
||||
".": {
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./invariant": {
|
||||
"types": "./lib/types/invariant.d.ts",
|
||||
"default": "./lib/invariant.js"
|
||||
},
|
||||
"./client": {
|
||||
"types": "./lib/types/client/index.d.ts",
|
||||
"default": "./lib/client.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": ["lib/index.js", "lib/invariant.js", "lib/client.js", "lib/types/**/*.d.ts"],
|
||||
"scripts": { "bundle": "tsdown", "watch": "tsdown --watch" },
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/invariant.js",
|
||||
"lib/client.js",
|
||||
"lib/types/**/*.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"bundle": "tsdown",
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
@@ -30,8 +49,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-commands": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"react": "^18.2.0"
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
|
||||
Generated
+101
-114
@@ -358,12 +358,6 @@ importers:
|
||||
'@deepseek-ai/dsh-client-web':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/client/web
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1(react@18.3.1)
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis-plugin-group':
|
||||
specifier: workspace:^
|
||||
@@ -404,6 +398,12 @@ importers:
|
||||
playwright:
|
||||
specifier: ^1.49.0
|
||||
version: 1.61.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1(react@18.3.1)
|
||||
typescript:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
@@ -1607,15 +1607,6 @@ importers:
|
||||
'@deepseek-ai/dsh-tools':
|
||||
specifier: workspace:^
|
||||
version: link:../../core/tools
|
||||
immer:
|
||||
specifier: ^10.1.1
|
||||
version: 10.2.0
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
zustand:
|
||||
specifier: ~4.4.7
|
||||
version: 4.4.7(@types/react@18.3.31)(immer@10.2.0)(react@18.3.1)
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -1638,6 +1629,15 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
immer:
|
||||
specifier: ^10.1.1
|
||||
version: 10.2.0
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
zustand:
|
||||
specifier: ~4.4.7
|
||||
version: 4.4.7(@types/react@18.3.31)(immer@10.2.0)(react@18.3.1)
|
||||
|
||||
packages/client/schema-form:
|
||||
dependencies:
|
||||
@@ -1705,15 +1705,6 @@ importers:
|
||||
'@deepseek-ai/dsh-client-ui-primitives':
|
||||
specifier: workspace:^
|
||||
version: link:../ui-primitives
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1(react@18.3.1)
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -1727,12 +1718,17 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: ~18.3.0
|
||||
version: 18.3.7(@types/react@18.3.31)
|
||||
|
||||
packages/client/ui-commands:
|
||||
dependencies:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1(react@18.3.1)
|
||||
|
||||
packages/client/ui-commands:
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -1773,6 +1769,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
@@ -1785,9 +1784,6 @@ importers:
|
||||
'@deepseek-ai/schemastery':
|
||||
specifier: link:../../../vendor/schemastery
|
||||
version: link:../../../vendor/schemastery
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -1873,15 +1869,14 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-deliverables:
|
||||
dependencies:
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -1913,12 +1908,11 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-directory-picker-browse:
|
||||
dependencies:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -1950,6 +1944,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
@@ -2036,10 +2033,6 @@ importers:
|
||||
version: 18.3.1(react@18.3.1)
|
||||
|
||||
packages/client/ui-input-trigger:
|
||||
dependencies:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2065,15 +2058,14 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-jobs:
|
||||
dependencies:
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2102,6 +2094,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-layout:
|
||||
devDependencies:
|
||||
@@ -2326,13 +2321,25 @@ importers:
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-primitives:
|
||||
dependencies:
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
version: link:../../../vendor/cordis
|
||||
'@deepseek-ai/dsh-invariants':
|
||||
specifier: workspace:^
|
||||
version: link:../../runtime-diagnostics/invariants
|
||||
'@shikijs/langs':
|
||||
specifier: ^4.3.1
|
||||
version: 4.3.1
|
||||
'@types/mdast':
|
||||
specifier: ^4.0.4
|
||||
version: 4.0.4
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
'@types/react-dom':
|
||||
specifier: ~18.3.0
|
||||
version: 18.3.7(@types/react@18.3.31)
|
||||
anser:
|
||||
specifier: ^2.3.5
|
||||
version: 2.3.5
|
||||
@@ -2387,19 +2394,6 @@ importers:
|
||||
shiki:
|
||||
specifier: ^4.3.1
|
||||
version: 4.3.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
version: link:../../../vendor/cordis
|
||||
'@deepseek-ai/dsh-invariants':
|
||||
specifier: workspace:^
|
||||
version: link:../../runtime-diagnostics/invariants
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
'@types/react-dom':
|
||||
specifier: ~18.3.0
|
||||
version: 18.3.7(@types/react@18.3.31)
|
||||
|
||||
packages/client/ui-settings:
|
||||
dependencies:
|
||||
@@ -2446,9 +2440,6 @@ importers:
|
||||
'@deepseek-ai/schemastery':
|
||||
specifier: link:../../../vendor/schemastery
|
||||
version: link:../../../vendor/schemastery
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2489,6 +2480,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
@@ -2581,10 +2575,6 @@ importers:
|
||||
version: 18.3.1(react@18.3.1)
|
||||
|
||||
packages/client/ui-settings-plugins:
|
||||
dependencies:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2622,15 +2612,14 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-sidebar:
|
||||
dependencies:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2659,6 +2648,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
@@ -2724,10 +2716,6 @@ importers:
|
||||
version: 18.3.31
|
||||
|
||||
packages/client/ui-subagent:
|
||||
dependencies:
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2765,6 +2753,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-theme:
|
||||
dependencies:
|
||||
@@ -2777,9 +2768,6 @@ importers:
|
||||
'@deepseek-ai/schemastery':
|
||||
specifier: link:../../../vendor/schemastery
|
||||
version: link:../../../vendor/schemastery
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2814,15 +2802,14 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-tool:
|
||||
dependencies:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2863,6 +2850,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
@@ -2871,13 +2861,6 @@ importers:
|
||||
version: 18.3.1(react@18.3.1)
|
||||
|
||||
packages/client/ui-trajectory:
|
||||
dependencies:
|
||||
'@tanstack/react-virtual':
|
||||
specifier: ^3.14.9
|
||||
version: 3.14.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
diff:
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.0
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2912,12 +2895,18 @@ importers:
|
||||
'@deepseek-ai/dsh-tools':
|
||||
specifier: workspace:^
|
||||
version: link:../../core/tools
|
||||
'@tanstack/react-virtual':
|
||||
specifier: ^3.14.9
|
||||
version: 3.14.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
'@types/react-dom':
|
||||
specifier: ~18.3.0
|
||||
version: 18.3.7(@types/react@18.3.31)
|
||||
diff:
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.0
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
@@ -2942,12 +2931,6 @@ importers:
|
||||
'@deepseek-ai/dsh-client-ui-slots':
|
||||
specifier: workspace:^
|
||||
version: link:../ui-slots
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -2976,12 +2959,14 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
|
||||
packages/client/ui-workflow-run:
|
||||
dependencies:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-workflow-run:
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -3019,12 +3004,11 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-workspace:
|
||||
dependencies:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -3056,6 +3040,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
@@ -3083,12 +3070,6 @@ importers:
|
||||
'@deepseek-ai/dsh-client-web-react':
|
||||
specifier: workspace:^
|
||||
version: link:../web-react
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1(react@18.3.1)
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -3111,6 +3092,12 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: ~18.3.0
|
||||
version: 18.3.7(@types/react@18.3.31)
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1(react@18.3.1)
|
||||
typescript:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
@@ -3120,12 +3107,6 @@ importers:
|
||||
'@deepseek-ai/dsh-client-ui-slots':
|
||||
specifier: workspace:^
|
||||
version: link:../ui-slots
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
use-sync-external-store:
|
||||
specifier: 1.2.0
|
||||
version: 1.2.0(react@18.3.1)
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:^
|
||||
@@ -3136,6 +3117,12 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
use-sync-external-store:
|
||||
specifier: 1.2.0
|
||||
version: 1.2.0(react@18.3.1)
|
||||
|
||||
packages/code-runtime/code-runtime:
|
||||
devDependencies:
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* The external packages a published browser artifact carries a copy of.
|
||||
*
|
||||
* Read from the real build configurations rather than declared by hand: each
|
||||
* `lib/client.js` plugin bundle is driven through its own `tsdown.config.ts`, and
|
||||
* the shell `dist` through `apps/web`'s Vite config. A recording plugin resolves
|
||||
* every bare specifier as external and notes it, so the pass walks our own source
|
||||
* and stops at the package boundary — which is both fast (about two seconds for
|
||||
* the whole repository) and exactly the direct-dependency granularity
|
||||
* THIRD_PARTY_NOTICES.md discloses. Erased type imports never appear, because the
|
||||
* transform drops them before resolution.
|
||||
*
|
||||
* Workspace names are followed only on the Vite side, where the shell's aliases
|
||||
* map them to source: that is how a browser-only library's own third-party
|
||||
* imports — katex and shiki through `ui-primitives`, for one — become visible. A
|
||||
* plugin bundle keeps them external, matching the frozen module table it is built
|
||||
* against; the wire layers it inlines are host packages that declare their own
|
||||
* dependencies, so nothing goes undisclosed.
|
||||
*
|
||||
* A specifier is recorded only once the host resolves it to a file inside a
|
||||
* package. A bundler's own virtual module has no package behind it —
|
||||
* `vite/modulepreload-polyfill` is generated by a Vite plugin rather than shipped
|
||||
* as a file, so the polyfill in the published `dist` is build glue in the same
|
||||
* category as an emitted TypeScript helper, not a redistributed copy of Vite.
|
||||
*
|
||||
* The pass runs on a clean tree, as a static gate must. The shell's Vite config
|
||||
* aliases a few workspace packages to source; every other workspace name would
|
||||
* resolve through `node_modules` to a `lib/` entry the real build has emitted but
|
||||
* a clean checkout has not, so this module resolves those names to their own
|
||||
* source instead. `lib/` is compiled from `src/`, so the third-party edges the
|
||||
* pass records are the same either way.
|
||||
*
|
||||
* rolldown is resolved through tsdown deliberately: the dry run must use the
|
||||
* exact bundler the real build uses, which a separate root pin could drift from.
|
||||
*/
|
||||
|
||||
import { existsSync, globSync, readFileSync } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import { dirname, join } from 'node:path'
|
||||
|
||||
/** The plugin-context member the recorder needs to resolve before recording. */
|
||||
interface ResolveContext {
|
||||
resolve: (
|
||||
source: string,
|
||||
importer: string,
|
||||
options: { skipSelf: boolean },
|
||||
) => Promise<{ id: string } | null>
|
||||
}
|
||||
|
||||
/** A rolldown/Vite plugin shape, narrowed to what the recorder needs. */
|
||||
interface RecorderPlugin {
|
||||
name: string
|
||||
enforce?: 'pre'
|
||||
resolveId: (
|
||||
this: ResolveContext,
|
||||
source: string,
|
||||
importer: string | undefined,
|
||||
) => Promise<{ id: string; external: true } | null>
|
||||
}
|
||||
|
||||
/**
|
||||
* The package a resolved module file belongs to.
|
||||
* @param file - absolute path of a resolved module.
|
||||
* @returns the package name, or undefined when the file is not inside a package.
|
||||
*/
|
||||
function packageOfFile(file: string): string | undefined {
|
||||
const marker = file.lastIndexOf('node_modules/')
|
||||
if (marker < 0) return undefined
|
||||
const rest = file.slice(marker + 'node_modules/'.length)
|
||||
const parts = rest.split('/')
|
||||
return rest.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Source aliases for the workspace packages the shell does not already alias.
|
||||
*
|
||||
* A clean checkout has no `lib/`, so a workspace name would otherwise resolve
|
||||
* through `node_modules` to an entry that does not exist yet. Aliases are the
|
||||
* right seam rather than a plugin hook, because Vite resolves a stylesheet
|
||||
* `@import` through them too — the theme package publishes its stylesheets from
|
||||
* `lib/styles/`. `lib/` is compiled from `src/`, so the third-party edges the
|
||||
* pass records are the same either way.
|
||||
* @param root - repository root.
|
||||
* @param existing - the shell's own alias patterns, whose entry choices win.
|
||||
* @returns alias entries mapping each remaining workspace name to its source.
|
||||
*/
|
||||
function workspaceSourceAliases(root: string, existing: readonly string[]): { find: RegExp | string; replacement: string }[] {
|
||||
const aliases: { find: RegExp | string; replacement: string }[] = []
|
||||
for (const pattern of ['packages/*/*/package.json', 'vendor/*/package.json']) {
|
||||
for (const relative of globSync(pattern, { cwd: root })) {
|
||||
const dir = join(root, dirname(relative))
|
||||
const manifest = JSON.parse(readFileSync(join(root, relative), 'utf8')) as Manifest & { name?: string }
|
||||
const name = manifest.name
|
||||
if (name === undefined || !existsSync(join(dir, 'src'))) continue
|
||||
if (existing.some(find => find.includes(name))) continue
|
||||
const root_ = manifest.exports?.['.']
|
||||
const target = typeof root_ === 'string' ? root_ : root_?.default
|
||||
const stem = (target ?? './lib/index.js')
|
||||
.replace(/^\.\/lib\/types\//, '').replace(/^\.\/lib\//, '').replace(/\.js$/, '')
|
||||
const entry = [`${stem}.ts`, `${stem}.tsx`, `${stem}/index.ts`, `${stem}/index.tsx`]
|
||||
.map(candidate => join(dir, 'src', candidate))
|
||||
.find(candidate => existsSync(candidate))
|
||||
// The subpath prefix carries `./client`, `./types`, and `./styles/*` alike:
|
||||
// each published subpath mirrors a path under `src/`.
|
||||
aliases.push({ find: `${name}/`, replacement: `${join(dir, 'src')}/` })
|
||||
if (entry !== undefined) aliases.push({ find: new RegExp(`^${name.replaceAll('/', '\\/')}$`), replacement: entry })
|
||||
}
|
||||
}
|
||||
return aliases
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the plugin that records bare specifiers and stops the walk at them.
|
||||
* @param seen - set the recorder adds package names to.
|
||||
* @returns the recording plugin.
|
||||
*/
|
||||
function recorder(seen: Set<string>): RecorderPlugin {
|
||||
return {
|
||||
name: 'dsh-record-direct-externals',
|
||||
enforce: 'pre',
|
||||
async resolveId(source, importer) {
|
||||
if (importer === undefined) return null // the entry itself
|
||||
if (source.startsWith('.') || source.startsWith('/') || source.startsWith('\0')) return null
|
||||
if (source.startsWith('virtual:') || source.includes('?')) return null
|
||||
// A workspace name that reaches here is one no alias mapped to source, so
|
||||
// nothing of ours is left to walk; it is never a third-party disclosure.
|
||||
if (source.startsWith('@deepseek-ai/')) return { id: source, external: true }
|
||||
if (source.startsWith('node:')) return { id: source, external: true }
|
||||
if (!source.startsWith('@deepseek-ai/')) {
|
||||
const resolved = await this.resolve(source, importer, { skipSelf: true })
|
||||
const name = resolved === null ? undefined : packageOfFile(resolved.id)
|
||||
if (name !== undefined) seen.add(name)
|
||||
}
|
||||
return { id: source, external: true }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
interface Manifest {
|
||||
exports?: Record<string, { default?: string } | string | null>
|
||||
files?: string[]
|
||||
}
|
||||
|
||||
/** Read one workspace manifest. */
|
||||
function manifestOf(dir: string): Manifest {
|
||||
return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as Manifest
|
||||
}
|
||||
|
||||
/** Whether a manifest publishes a tsdown browser bundle at `lib/client.js`. */
|
||||
function publishesClientBundle(manifest: Manifest): boolean {
|
||||
const target = manifest.exports?.['./client']
|
||||
return typeof target === 'object' && target !== null && target.default === './lib/client.js'
|
||||
}
|
||||
|
||||
/**
|
||||
* Record every external package the plugin client bundles carry.
|
||||
* @param root - repository root.
|
||||
* @param seen - set the recorder adds package names to.
|
||||
*/
|
||||
async function collectFromClientBundles(root: string, seen: Set<string>): Promise<void> {
|
||||
const requireFromTsdown = createRequire(createRequire(import.meta.url).resolve('tsdown'))
|
||||
const { rolldown } = await import(requireFromTsdown.resolve('rolldown')) as {
|
||||
rolldown: (options: Record<string, unknown>) => Promise<{
|
||||
generate: (output: Record<string, unknown>) => Promise<unknown>
|
||||
close: () => Promise<void>
|
||||
}>
|
||||
}
|
||||
|
||||
for (const relative of globSync('packages/*/*/tsdown.config.ts', { cwd: root }).sort()) {
|
||||
const dir = join(root, dirname(relative))
|
||||
if (!publishesClientBundle(manifestOf(dir))) continue
|
||||
const loaded = await import(join(root, relative)) as { default: unknown }
|
||||
const factory = loaded.default
|
||||
const configs = (typeof factory === 'function'
|
||||
? (factory as (inline: { env: Record<string, string> }) => unknown[])({ env: {} })
|
||||
: [factory]) as { name?: string; entry?: unknown; plugins?: unknown[] }[]
|
||||
// The `/client` config is the browser bundle; its siblings emit the node half.
|
||||
const client = configs.find(config => config.name?.endsWith('/client') === true)
|
||||
if (client === undefined) continue
|
||||
const bundle = await rolldown({
|
||||
cwd: dir,
|
||||
input: client.entry,
|
||||
plugins: [recorder(seen), ...(client.plugins ?? [])],
|
||||
platform: 'browser',
|
||||
})
|
||||
await bundle.generate({ format: 'cjs', minify: false, sourcemap: false })
|
||||
await bundle.close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record every external package the prebuilt shell bundle carries.
|
||||
* @param root - repository root.
|
||||
* @param seen - set the recorder adds package names to.
|
||||
*/
|
||||
async function collectFromShellBundle(root: string, seen: Set<string>): Promise<void> {
|
||||
for (const relative of globSync('apps/*/vite.config.ts', { cwd: root }).sort()) {
|
||||
const dir = join(root, dirname(relative))
|
||||
// Vite belongs to the app that builds with it, so it resolves from there.
|
||||
const { build, resolveConfig } = await import(createRequire(join(dir, 'package.json')).resolve('vite')) as {
|
||||
build: (options: Record<string, unknown>) => Promise<unknown>
|
||||
resolveConfig: (options: Record<string, unknown>, command: string) => Promise<{
|
||||
resolve: { alias: { find: string | RegExp }[] }
|
||||
}>
|
||||
}
|
||||
// The shell already aliases some workspace names to source, and its entry
|
||||
// choices win: a stylesheet `@import` resolves through aliases rather than a
|
||||
// plugin hook, so only the names it leaves out get one from here.
|
||||
const resolved = await resolveConfig({ root: dir, logLevel: 'error' }, 'build')
|
||||
await build({
|
||||
root: dir,
|
||||
logLevel: 'error',
|
||||
plugins: [recorder(seen)],
|
||||
resolve: { alias: workspaceSourceAliases(root, resolved.resolve.alias.map(entry => String(entry.find))) },
|
||||
build: { write: false, minify: false, sourcemap: false, reportCompressedSize: false },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The external packages a published browser artifact carries a copy of.
|
||||
* @param root - repository root.
|
||||
* @returns package names, workspace names excluded.
|
||||
*/
|
||||
export async function browserBundledExternals(root: string): Promise<Set<string>> {
|
||||
const seen = new Set<string>()
|
||||
await collectFromClientBundles(root, seen)
|
||||
await collectFromShellBundle(root, seen)
|
||||
return seen
|
||||
}
|
||||
@@ -24,11 +24,12 @@ describe('THIRD_PARTY_NOTICES.md', () => {
|
||||
// already runs in the test lane, so the check costs no extra CI process.
|
||||
// Pre-commit regenerates the file whenever a manifest is staged, so reaching
|
||||
// this assertion means the notices were committed without that hook.
|
||||
it('matches what the generator produces from the current manifests', () => {
|
||||
const generated = render()
|
||||
it('matches what the generator produces from the current manifests', async () => {
|
||||
const generated = await render()
|
||||
expect(generated).toContain('It depends on the third-party software listed below.')
|
||||
expect(readFileSync(resolve(root, 'THIRD_PARTY_NOTICES.md'), 'utf8'), 'stale notices — run `pnpm run gen-third-party-notices`').toBe(generated)
|
||||
})
|
||||
// Driving the two real bundlers to learn what ships costs a few seconds.
|
||||
}, 60_000)
|
||||
})
|
||||
|
||||
/** Build the (manifests, names) pair `tierExternalDeps` consumes. */
|
||||
@@ -67,6 +68,23 @@ describe('tierExternalDeps', () => {
|
||||
]))
|
||||
})
|
||||
|
||||
it('keeps a devDependency runtime when a published browser artifact carries it', () => {
|
||||
const { manifests, names } = workspace({
|
||||
// The client build inlines these, so a copy ships even though no manifest
|
||||
// resolves the specifier at run time.
|
||||
'packages/client/ui-primitives/package.json': {
|
||||
name: '@deepseek-ai/dsh-client-ui-primitives',
|
||||
devDependencies: { katex: '^0.16', 'test-only-helper': '^1' },
|
||||
},
|
||||
})
|
||||
|
||||
expect(tierExternalDeps(manifests, names, new Set(['katex']))).toEqual(new Map([
|
||||
['tsx', true],
|
||||
['katex', true],
|
||||
['test-only-helper', false],
|
||||
]))
|
||||
})
|
||||
|
||||
it('keeps a package runtime when any shipping area declares it, and excludes workspace links', () => {
|
||||
const { manifests, names } = workspace({
|
||||
'package.json': { devDependencies: { shared: '^1' } },
|
||||
|
||||
@@ -13,6 +13,7 @@ import { resolve } from 'node:path'
|
||||
import * as yaml from 'js-yaml'
|
||||
import { parse as parseToml, type TomlTableWithoutBigInt, type TomlValueWithoutBigInt } from 'smol-toml'
|
||||
import parseSpdx from 'spdx-expression-parse'
|
||||
import { browserBundledExternals } from './browser-bundled-externals.ts'
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
const OUT = 'THIRD_PARTY_NOTICES.md'
|
||||
@@ -347,15 +348,17 @@ function normalizeRepo(raw: string | undefined): string | undefined {
|
||||
}
|
||||
|
||||
/**
|
||||
* External npm dependencies, tiered by which workspace area declares them at
|
||||
* runtime: a package is runtime when any manifest outside `DEV_ONLY_AREAS`
|
||||
* names it in `dependencies`/`optionalDependencies`. A package declared only
|
||||
* by tooling, test infrastructure, the website, or the demo leaves — whatever
|
||||
* the declaring section is called — is development-only.
|
||||
* External npm dependencies, tiered by what reaches a user: a package is runtime
|
||||
* when any manifest outside `DEV_ONLY_AREAS` names it in
|
||||
* `dependencies`/`optionalDependencies`, or when a published browser artifact
|
||||
* carries a copy of it. A package declared only by tooling, test infrastructure,
|
||||
* the website, or the demo leaves — whatever the declaring section is called, and
|
||||
* with no shipped artifact carrying it — is development-only.
|
||||
* @returns every external dependency with its tier and metadata.
|
||||
*/
|
||||
function collectNpmDeps(): ExternalDep[] {
|
||||
async function collectNpmDeps(): Promise<ExternalDep[]> {
|
||||
const { manifests, names } = loadWorkspaceManifests()
|
||||
return [...tierExternalDeps(manifests, names)]
|
||||
return [...tierExternalDeps(manifests, names, await browserBundledExternals(root))]
|
||||
.filter(([name]) => !FIRST_PARTY.has(name))
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([name, runtime]) => ({ name, ...installedMetadata(name), runtime }))
|
||||
@@ -363,11 +366,23 @@ function collectNpmDeps(): ExternalDep[] {
|
||||
|
||||
/**
|
||||
* Tier every external dependency the workspace declares.
|
||||
*
|
||||
* A package a published browser artifact carries is runtime whatever section
|
||||
* declares it: the client build inlines its code, or the shell `dist` answers it
|
||||
* from the frozen module table, so a copy is redistributed even though nothing on
|
||||
* a user's machine resolves the specifier. Those packages are declared as
|
||||
* `devDependencies` — `verify-client-runtime-deps` owns that rule — and tiering
|
||||
* them by section alone would understate the notice.
|
||||
* @param manifests - workspace manifests keyed by repository-relative path.
|
||||
* @param names - every workspace package name, which never counts as external.
|
||||
* @param bundled - external packages a published browser artifact carries.
|
||||
* @returns each external package mapped to whether it is a runtime dependency.
|
||||
*/
|
||||
export function tierExternalDeps(manifests: Map<string, Manifest>, names: Set<string>): Map<string, boolean> {
|
||||
export function tierExternalDeps(
|
||||
manifests: Map<string, Manifest>,
|
||||
names: Set<string>,
|
||||
bundled: ReadonlySet<string> = new Set(),
|
||||
): Map<string, boolean> {
|
||||
const tiers = new Map<string, boolean>()
|
||||
// `tsx` is runtime by fiat: the root source-run scripts execute through its ESM hook.
|
||||
tiers.set('tsx', true)
|
||||
@@ -376,7 +391,7 @@ export function tierExternalDeps(manifests: Map<string, Manifest>, names: Set<st
|
||||
for (const kind of ALL_KINDS) {
|
||||
for (const [dep, range] of Object.entries(manifest[kind] ?? {})) {
|
||||
if (names.has(dep) || range.startsWith('workspace:')) continue
|
||||
const runtime = !devOnly && (RUNTIME_KINDS as readonly string[]).includes(kind)
|
||||
const runtime = bundled.has(dep) || (!devOnly && (RUNTIME_KINDS as readonly string[]).includes(kind))
|
||||
tiers.set(dep, (tiers.get(dep) ?? false) || runtime)
|
||||
}
|
||||
}
|
||||
@@ -660,9 +675,9 @@ ${rows.join('\n')}
|
||||
* Render the complete notices document.
|
||||
* @returns the exact bytes `THIRD_PARTY_NOTICES.md` must hold.
|
||||
*/
|
||||
export function render(): string {
|
||||
export async function render(): Promise<string> {
|
||||
verifyBuildTimePins()
|
||||
const npm = collectNpmDeps()
|
||||
const npm = await collectNpmDeps()
|
||||
const runtimeDeps = npm.filter(dep => dep.runtime)
|
||||
const devDeps = npm.filter(dep => !dep.runtime)
|
||||
const vendored = collectVendored()
|
||||
@@ -707,7 +722,7 @@ ${vendored.map(row => `| \`${row.npmName}\` | \`${row.upstreamName}\` | [${row.u
|
||||
|
||||
## Runtime npm dependencies
|
||||
|
||||
External packages that a workspace package resolves at runtime. The tier covers every plugin a user can mount from \`cordis.yml\` — not only what the \`dsh\` CLI, Web UI, and Python SDK runtime load by default.
|
||||
External packages that reach a user: a workspace package resolves them at runtime, or a published browser artifact carries a copy of their code. The tier covers every plugin a user can mount from \`cordis.yml\` — not only what the \`dsh\` CLI, Web UI, and Python SDK runtime load by default — and the packages the client build inlines into a plugin bundle or the shell \`dist\`, which are declared as \`devDependencies\` because nothing on a user's machine resolves their specifiers.
|
||||
|
||||
${renderNpmTable(runtimeDeps)}
|
||||
|
||||
@@ -718,7 +733,7 @@ ${renderClaudeDistribution(claudeDistribution)}
|
||||
|
||||
## Development-only npm dependencies
|
||||
|
||||
External packages **directly declared** only by repository tooling, test infrastructure, the documentation site, the demo leaves, or the native launcher's build workspace. No shipped surface names them itself. A package here may still be pulled in transitively by a runtime dependency — \`pnpm-lock.yaml\` is the authority on the full closure — so this tier records who declares a package, not what a build ultimately bundles.
|
||||
External packages **directly declared** only by repository tooling, test infrastructure, the documentation site, the demo leaves, or the native launcher's build workspace, and carried by no published artifact. No shipped surface names them itself. A package here may still be pulled in transitively by a runtime dependency — \`pnpm-lock.yaml\` is the authority on the full closure — so this tier records who declares a package, not what a build ultimately bundles.
|
||||
|
||||
${renderNpmTable(devDeps)}
|
||||
${renderNonPermissiveNote(nonPermissiveDev)}
|
||||
@@ -746,8 +761,8 @@ ${BUILD_TIME_TOOLS.map(tool => `| [\`${tool.name}\`](${tool.repo}) | ${tool.lice
|
||||
/** CLI entry: default writes the notices, `--check` fails if the committed copy
|
||||
* is stale. Guarded behind an entry-point check so importing this module for
|
||||
* tests neither regenerates the committed file nor calls process.exit. */
|
||||
function main(): void {
|
||||
const content = render()
|
||||
async function main(): Promise<void> {
|
||||
const content = await render()
|
||||
if (process.argv.includes('--check')) {
|
||||
let committed: string | null = null
|
||||
try {
|
||||
@@ -771,5 +786,5 @@ function main(): void {
|
||||
|
||||
// Run only when invoked as a script, not when imported by a test.
|
||||
if (process.argv[1] !== undefined && import.meta.filename === resolve(process.argv[1])) {
|
||||
main()
|
||||
await main()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
/**
|
||||
* Keep browser-only external packages out of installed dependency sections.
|
||||
*
|
||||
* A browser artifact resolves nothing on the user's machine: tsdown inlines
|
||||
* every non-platform specifier into `lib/client.js`, the shell `dist` answers
|
||||
* `PLATFORM_MODULES` from its frozen module table, and Vite inlines the shell's
|
||||
* own imports into that published `dist`. A specifier only browser source
|
||||
* reaches is therefore a build-time input and belongs in `devDependencies`,
|
||||
* because npm installs `dependencies` and non-optional `peerDependencies` for
|
||||
* every consumer of the published package.
|
||||
*
|
||||
* Each face is walked from the entries the manifest publishes, not by a
|
||||
* directory rule, so a module under `src/` that only the browser entry reaches
|
||||
* counts as browser source:
|
||||
*
|
||||
* `./client` is `lib/client.js` host: the other export targets; browser: the bundle
|
||||
* `packages/client/*` with no browser-only library: host is `src/invariant.ts`,
|
||||
* `./client` export the companion the host mounts; `.` is browser code
|
||||
* no `.` export, ships a `dist` prebuilt browser bundle: no host face at all
|
||||
*
|
||||
* Only external packages are subject: they are what an install downloads. A
|
||||
* workspace name stays where its manifest puts it, because that declaration also
|
||||
* states which package supplies an injected service or a mounted Remote
|
||||
* contribution, and the app installs it either way. A reference from the host
|
||||
* face, an erased type import included, likewise keeps a declaration in place.
|
||||
*
|
||||
* Run: pnpm exec tsx scripts/verify-client-runtime-deps.ts [--json]
|
||||
*/
|
||||
|
||||
import { existsSync, globSync, readFileSync } from 'node:fs'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import ts from 'typescript'
|
||||
import { TypeScriptProject, type CompilerFace } from './ts-project.ts'
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
|
||||
/**
|
||||
* `@deepseek-ai/cordis` placement belongs to check-workspace-constraints, which
|
||||
* requires it as a peerDependency plus devDependency of every harness package
|
||||
* regardless of face.
|
||||
*/
|
||||
const PLACEMENT_OWNED_ELSEWHERE = new Set(['@deepseek-ai/cordis'])
|
||||
|
||||
/** Dependency sections npm installs for a consumer of the published package. */
|
||||
const INSTALLED_SECTIONS = ['dependencies', 'peerDependencies'] as const
|
||||
|
||||
type Section = (typeof INSTALLED_SECTIONS)[number]
|
||||
|
||||
interface Manifest {
|
||||
name?: string
|
||||
files?: string[]
|
||||
exports?: Record<string, unknown>
|
||||
dependencies?: Record<string, string>
|
||||
peerDependencies?: Record<string, string>
|
||||
peerDependenciesMeta?: Record<string, { optional?: boolean }>
|
||||
}
|
||||
|
||||
/** How a package reaches the browser, which fixes the entries Node can load. */
|
||||
type Kind = 'bundle-half' | 'browser-library' | 'prebuilt-dist'
|
||||
|
||||
/** What settles an external specifier as build-time only. */
|
||||
type Reached = 'browser' | 'nothing'
|
||||
|
||||
interface Violation {
|
||||
readonly section: Section
|
||||
readonly dep: string
|
||||
readonly reached: Reached
|
||||
/** Whether the browser face names it, which decides dev-move versus deletion. */
|
||||
readonly browserReferenced: boolean
|
||||
}
|
||||
|
||||
interface Offender {
|
||||
readonly name: string
|
||||
readonly dir: string
|
||||
readonly kind: Kind
|
||||
readonly violations: Violation[]
|
||||
}
|
||||
|
||||
/** Why each class needs no install, for the failure report. */
|
||||
const REASON: Record<Reached, string> = {
|
||||
browser: 'only a browser artifact reaches it, and that resolves nothing on the user machine',
|
||||
nothing: 'no reference names it at all',
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify a package by the browser artifact it produces.
|
||||
* @param dir - repository-relative package directory.
|
||||
* @param manifest - the package manifest.
|
||||
* @returns the package kind, or undefined when the package has no browser face.
|
||||
*/
|
||||
function kindOf(dir: string, manifest: Manifest): Kind | undefined {
|
||||
if (manifest.exports?.['./client'] !== undefined) return 'bundle-half'
|
||||
if (dir.startsWith('packages/client/')) return 'browser-library'
|
||||
const shipsDist = (manifest.files ?? []).some(entry => entry === 'dist' || entry.startsWith('dist/'))
|
||||
if (shipsDist && manifest.exports?.['.'] === undefined) return 'prebuilt-dist'
|
||||
return undefined
|
||||
}
|
||||
|
||||
/** The bare package name a specifier names, keeping a leading scope. */
|
||||
function packageOf(specifier: string): string {
|
||||
const parts = specifier.split('/')
|
||||
return specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0] ?? specifier
|
||||
}
|
||||
|
||||
/** One compiler face's bound program plus its module resolution state. */
|
||||
interface Face {
|
||||
readonly project: TypeScriptProject
|
||||
readonly host: ts.CompilerHost
|
||||
readonly cache: ts.ModuleResolutionCache
|
||||
}
|
||||
|
||||
const faces = new Map<CompilerFace, Face>()
|
||||
for (const face of ['host', 'client'] as const) {
|
||||
const project = new TypeScriptProject(root, face)
|
||||
const options = project.program.getCompilerOptions()
|
||||
faces.set(face, {
|
||||
project,
|
||||
host: ts.createCompilerHost(options, false),
|
||||
cache: ts.createModuleResolutionCache(root, fileName => fileName, options),
|
||||
})
|
||||
}
|
||||
|
||||
/** Which face's program bound each workspace module, keyed by absolute path. */
|
||||
const boundIn = new Map<string, CompilerFace>()
|
||||
for (const [face, { project }] of faces) {
|
||||
for (const sourceFile of project.sourceFiles()) {
|
||||
if (sourceFile.isDeclarationFile) continue
|
||||
if (!boundIn.has(sourceFile.fileName)) boundIn.set(sourceFile.fileName, face)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read every module specifier one source file names.
|
||||
*
|
||||
* An import clause is not the only way to reach a package: `require`,
|
||||
* `require.resolve`, and a dynamic `import()` on a literal each name one, and a
|
||||
* type-only import still names a package the build must resolve.
|
||||
* @param sourceFile - a bound source file.
|
||||
* @returns every specifier, relative ones included.
|
||||
*/
|
||||
function specifiersOf(sourceFile: ts.SourceFile): string[] {
|
||||
const specifiers: string[] = []
|
||||
const visit = (node: ts.Node): void => {
|
||||
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node) || ts.isImportEqualsDeclaration(node)) {
|
||||
const specifier = ts.isImportEqualsDeclaration(node)
|
||||
? (ts.isExternalModuleReference(node.moduleReference) ? node.moduleReference.expression : undefined)
|
||||
: node.moduleSpecifier
|
||||
if (specifier !== undefined && ts.isStringLiteralLike(specifier)) specifiers.push(specifier.text)
|
||||
} else if (ts.isCallExpression(node)) {
|
||||
const target = node.expression
|
||||
const isRequire = ts.isIdentifier(target) && target.text === 'require'
|
||||
const isRequireResolve = ts.isPropertyAccessExpression(target)
|
||||
&& ts.isIdentifier(target.expression) && target.expression.text === 'require'
|
||||
&& target.name.text === 'resolve'
|
||||
const argument = node.arguments[0]
|
||||
if ((isRequire || isRequireResolve || target.kind === ts.SyntaxKind.ImportKeyword)
|
||||
&& argument !== undefined && ts.isStringLiteralLike(argument)) {
|
||||
specifiers.push(argument.text)
|
||||
}
|
||||
}
|
||||
ts.forEachChild(node, visit)
|
||||
}
|
||||
visit(sourceFile)
|
||||
return specifiers
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk one face from its entries and collect the packages it names.
|
||||
* @param entries - absolute entry module paths.
|
||||
* @param packageDir - absolute package directory; the walk stops at its edge.
|
||||
* @returns package names the walk reaches.
|
||||
*/
|
||||
function walk(entries: readonly string[], packageDir: string): Set<string> {
|
||||
const found = new Set<string>()
|
||||
const seen = new Set<string>()
|
||||
const queue = entries.filter(entry => boundIn.has(entry))
|
||||
while (queue.length > 0) {
|
||||
const file = queue.pop()
|
||||
if (file === undefined || seen.has(file)) continue
|
||||
seen.add(file)
|
||||
const faceName = boundIn.get(file)
|
||||
const face = faceName === undefined ? undefined : faces.get(faceName)
|
||||
const sourceFile = face?.project.program.getSourceFile(file)
|
||||
if (face === undefined || sourceFile === undefined) continue
|
||||
|
||||
for (const specifier of specifiersOf(sourceFile)) {
|
||||
if (!specifier.startsWith('.')) {
|
||||
if (!specifier.startsWith('node:')) found.add(packageOf(specifier))
|
||||
continue
|
||||
}
|
||||
const resolved = ts.resolveModuleName(
|
||||
specifier, file, face.project.program.getCompilerOptions(), face.host, face.cache,
|
||||
).resolvedModule?.resolvedFileName
|
||||
// A relative specifier resolving outside the package is a packaging error
|
||||
// verify-package-paths owns; either way it is not this package's own module.
|
||||
if (resolved !== undefined && resolved.startsWith(`${packageDir}/`)) queue.push(resolved)
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
/**
|
||||
* The source module behind one published JavaScript export target.
|
||||
*
|
||||
* `lib/` holds the tsdown bundles and `lib/types/` the tsc emit, so both
|
||||
* prefixes lead back to one `src` module.
|
||||
* @param dir - absolute package directory.
|
||||
* @param emitted - the export target, as written in the manifest.
|
||||
* @returns the absolute source path, or undefined when nothing in `src` emits it.
|
||||
*/
|
||||
function sourceBehind(dir: string, emitted: string): string | undefined {
|
||||
const stem = emitted.replace(/^\.\/lib\/types\//, '').replace(/^\.\/lib\//, '').replace(/\.js$/, '')
|
||||
return [`src/${stem}.ts`, `src/${stem}.tsx`, `src/${stem}/index.ts`, `src/${stem}/index.tsx`]
|
||||
.map(candidate => join(dir, candidate))
|
||||
.find(candidate => existsSync(candidate))
|
||||
}
|
||||
|
||||
interface Entries {
|
||||
readonly host: string[]
|
||||
readonly browser: string[]
|
||||
/**
|
||||
* Published JavaScript entries no `src` module emits — a generated artifact
|
||||
* such as `lib/typert.host.js`, whose own runtime imports are invisible here.
|
||||
*/
|
||||
readonly generated: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry modules of each face, derived from what the manifest publishes.
|
||||
* @param dir - absolute package directory.
|
||||
* @param manifest - the package manifest.
|
||||
* @param kind - the package kind.
|
||||
* @returns absolute entry module paths per face, plus unmapped published entries.
|
||||
*/
|
||||
function faceEntries(dir: string, manifest: Manifest, kind: Kind): Entries {
|
||||
if (kind === 'prebuilt-dist') return { host: [], browser: [], generated: [] }
|
||||
if (kind === 'browser-library') {
|
||||
return { host: [join(dir, 'src/invariant.ts')], browser: [join(dir, 'src/index.ts')], generated: [] }
|
||||
}
|
||||
const host = [join(dir, 'src/index.ts'), join(dir, 'src/invariant.ts')]
|
||||
const browser: string[] = []
|
||||
const generated: string[] = []
|
||||
for (const [key, target] of Object.entries(manifest.exports ?? {})) {
|
||||
if (key === '.' || key === './package.json' || key.includes('*')) continue
|
||||
const emitted = typeof target === 'string' ? target : (target as { default?: unknown }).default
|
||||
if (typeof emitted !== 'string' || !emitted.endsWith('.js')) continue
|
||||
// Keyed on the artifact path, not the subpath name: `./client` is the tsdown
|
||||
// browser bundle only when it resolves to lib/client.js, while other packages
|
||||
// publish a plain browser-shared module under the same subpath.
|
||||
const source = emitted === './lib/client.js'
|
||||
? sourceBehind(dir, './lib/client/index.js')
|
||||
: sourceBehind(dir, emitted)
|
||||
if (source === undefined) generated.push(`${key} -> ${emitted}`)
|
||||
else if (key === './client') browser.push(source)
|
||||
else host.push(source)
|
||||
}
|
||||
return { host, browser, generated }
|
||||
}
|
||||
|
||||
/** Every installed dependency of a manifest, paired with its section. */
|
||||
function installedDeps(manifest: Manifest): { section: Section; dep: string }[] {
|
||||
const deps: { section: Section; dep: string }[] = []
|
||||
for (const section of INSTALLED_SECTIONS) {
|
||||
for (const dep of Object.keys(manifest[section] ?? {})) {
|
||||
if (section === 'peerDependencies' && manifest.peerDependenciesMeta?.[dep]?.optional === true) continue
|
||||
if (PLACEMENT_OWNED_ELSEWHERE.has(dep)) continue
|
||||
deps.push({ section, dep })
|
||||
}
|
||||
}
|
||||
return deps
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a Loader config names a package as a whole word.
|
||||
* @param text - raw config text.
|
||||
* @param dep - package name to look for.
|
||||
* @returns true when the name appears outside a longer specifier.
|
||||
*/
|
||||
function namesPackage(text: string, dep: string): boolean {
|
||||
const escaped = dep.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
return new RegExp(`(^|[^\\w@/.-])${escaped}(?![\\w.-])`).test(text)
|
||||
}
|
||||
|
||||
interface Candidate {
|
||||
readonly name: string
|
||||
readonly relativeDir: string
|
||||
readonly manifest: Manifest
|
||||
readonly kind: Kind
|
||||
}
|
||||
|
||||
const candidates: Candidate[] = []
|
||||
for (const path of [
|
||||
...globSync('packages/*/*/package.json', { cwd: root }),
|
||||
...globSync('apps/*/package.json', { cwd: root }),
|
||||
].sort()) {
|
||||
const relativeDir = dirname(path)
|
||||
const manifest = JSON.parse(readFileSync(join(root, path), 'utf8')) as Manifest
|
||||
if (manifest.name === undefined) continue
|
||||
const kind = kindOf(relativeDir, manifest)
|
||||
if (kind !== undefined) candidates.push({ name: manifest.name, relativeDir, manifest, kind })
|
||||
}
|
||||
|
||||
const offenders: Offender[] = []
|
||||
const unchecked: string[] = []
|
||||
for (const { name, relativeDir, manifest, kind } of candidates) {
|
||||
const dir = join(root, relativeDir)
|
||||
const entries = faceEntries(dir, manifest, kind)
|
||||
// A generated Node entry carries runtime imports of its own that no source
|
||||
// states, so this package's declarations cannot be judged from `src` alone.
|
||||
if (entries.generated.length > 0) {
|
||||
unchecked.push(`${name}: generated entry ${entries.generated.join(', ')}`)
|
||||
continue
|
||||
}
|
||||
const host = walk(entries.host, dir)
|
||||
const browser = walk(entries.browser, dir)
|
||||
// A Loader row names its plugin package instead of importing it, so a config
|
||||
// the package owns is part of its host face. YAML keys carry no quotes, so
|
||||
// these are matched as whole names against the raw text.
|
||||
const configs = globSync('cordis*.yml', { cwd: dir }).map(config => readFileSync(join(dir, config), 'utf8'))
|
||||
|
||||
const violations = installedDeps(manifest)
|
||||
// A workspace name stays where the manifest puts it. Such a declaration also
|
||||
// states which package supplies an injected service, which Remote contribution
|
||||
// an assembly mounts, or which Loader row must resolve; the app installs the
|
||||
// package regardless, so moving one saves no download while deleting what
|
||||
// verify-runtime-closure and the Loader read. External packages are the
|
||||
// download, and this gate is about the download.
|
||||
.filter(({ dep }) => !dep.startsWith('@deepseek-ai/'))
|
||||
.filter(({ dep }) => !host.has(dep) && !configs.some(text => namesPackage(text, dep)))
|
||||
.map(({ section, dep }) => ({
|
||||
section,
|
||||
dep,
|
||||
browserReferenced: browser.has(dep),
|
||||
// A prebuilt bundle publishes no Node entry, so everything it declares is
|
||||
// build-time by construction, named in its Vite graph rather than in src.
|
||||
reached: kind === 'prebuilt-dist' || browser.has(dep) ? 'browser' as const : 'nothing' as const,
|
||||
}))
|
||||
if (violations.length > 0) offenders.push({ name, dir: relativeDir, kind, violations })
|
||||
}
|
||||
|
||||
if (process.argv.includes('--json')) {
|
||||
console.log(JSON.stringify(offenders, null, 2))
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
if (unchecked.length > 0) {
|
||||
console.log(`verify-client-runtime-deps: ${String(unchecked.length)} package(s) not checked, no source states their entry's imports:`)
|
||||
for (const entry of unchecked) console.log(` ${entry}`)
|
||||
}
|
||||
|
||||
if (offenders.length > 0) {
|
||||
const all = offenders.flatMap(offender => offender.violations)
|
||||
console.error(`verify-client-runtime-deps: ${String(all.length)} build-time specifier(s) in installed sections:`)
|
||||
for (const { name, dir, kind, violations } of offenders) {
|
||||
console.error(` ${name} (${dir}, ${kind})`)
|
||||
for (const { section, dep, reached } of violations) {
|
||||
console.error(` ${section}.${dep} -> devDependencies [${reached}]`)
|
||||
}
|
||||
}
|
||||
console.error('')
|
||||
for (const reached of ['browser', 'nothing'] as const) {
|
||||
const count = all.filter(violation => violation.reached === reached).length
|
||||
if (count > 0) console.error(` ${String(count).padStart(4)} ${reached}: ${REASON[reached]}`)
|
||||
}
|
||||
console.error('\nDeclaration rules: packages/client/AGENTS.md.')
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`verify-client-runtime-deps: browser-face specifiers are dev-only across ${String(candidates.length)} browser-facing packages.`)
|
||||
Reference in New Issue
Block a user