upstream-radar 0.33.11 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +69 -1029
  2. package/dist/src/cli.js +365 -19
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/dependency-index.d.ts +76 -0
  5. package/dist/src/dependency-index.d.ts.map +1 -0
  6. package/dist/src/dependency-index.js +199 -0
  7. package/dist/src/dependency-index.js.map +1 -0
  8. package/dist/src/dsh-case.d.ts +50 -0
  9. package/dist/src/dsh-case.d.ts.map +1 -0
  10. package/dist/src/dsh-case.js +88 -0
  11. package/dist/src/dsh-case.js.map +1 -0
  12. package/dist/src/dsh-review.d.ts +46 -0
  13. package/dist/src/dsh-review.d.ts.map +1 -0
  14. package/dist/src/dsh-review.js +209 -0
  15. package/dist/src/dsh-review.js.map +1 -0
  16. package/dist/src/graph.d.ts +5 -1
  17. package/dist/src/graph.d.ts.map +1 -1
  18. package/dist/src/graph.js +56 -10
  19. package/dist/src/graph.js.map +1 -1
  20. package/dist/src/index.d.ts +4 -1
  21. package/dist/src/index.d.ts.map +1 -1
  22. package/dist/src/index.js +3 -0
  23. package/dist/src/index.js.map +1 -1
  24. package/dist/src/npm.d.ts +11 -0
  25. package/dist/src/npm.d.ts.map +1 -1
  26. package/dist/src/npm.js +122 -12
  27. package/dist/src/npm.js.map +1 -1
  28. package/dist/src/render.d.ts.map +1 -1
  29. package/dist/src/render.js +19 -0
  30. package/dist/src/render.js.map +1 -1
  31. package/dist/src/scan.d.ts.map +1 -1
  32. package/dist/src/scan.js +41 -0
  33. package/dist/src/scan.js.map +1 -1
  34. package/dist/src/types.d.ts +10 -0
  35. package/dist/src/types.d.ts.map +1 -1
  36. package/dist/src/upstream-observer.d.ts +85 -0
  37. package/dist/src/upstream-observer.d.ts.map +1 -1
  38. package/dist/src/upstream-observer.js +374 -21
  39. package/dist/src/upstream-observer.js.map +1 -1
  40. package/dist/src/version.d.ts +1 -1
  41. package/dist/src/version.d.ts.map +1 -1
  42. package/dist/src/version.js +1 -1
  43. package/dist/src/version.js.map +1 -1
  44. package/docs/README.zh-CN.md +98 -22
  45. package/examples/cases/dsh-web-ui-issue-71/before/cordis.patch.yml +4 -0
  46. package/examples/cases/dsh-web-ui-issue-71/before/node_modules/@linxin666/dsh-client-ui-skin-center/cordis.patch.yml +1 -0
  47. package/examples/cases/dsh-web-ui-issue-71/before/node_modules/@linxin666/dsh-client-ui-skin-center/package.json +5 -0
  48. package/examples/cases/dsh-web-ui-issue-71/before/node_modules/@linxin666/dsh-web-ui-all/cordis.patch.yml +1 -0
  49. package/examples/cases/dsh-web-ui-issue-71/before/node_modules/@linxin666/dsh-web-ui-all/package.json +5 -0
  50. package/examples/cases/dsh-web-ui-issue-71/before/package.json +12 -0
  51. package/examples/cases/dsh-web-ui-issue-71/before/pnpm-lock.yaml +21 -0
  52. package/examples/cases/dsh-web-ui-issue-71/before/pnpm-workspace.yaml +7 -0
  53. package/examples/cases/dsh-web-ui-issue-71/fixed/cordis.patch.yml +3 -0
  54. package/examples/cases/dsh-web-ui-issue-71/fixed/node_modules/@linxin666/dsh-client-ui-skin-center/cordis.patch.yml +1 -0
  55. package/examples/cases/dsh-web-ui-issue-71/fixed/node_modules/@linxin666/dsh-client-ui-skin-center/package.json +5 -0
  56. package/examples/cases/dsh-web-ui-issue-71/fixed/node_modules/@linxin666/dsh-web-ui-all/cordis.patch.yml +1 -0
  57. package/examples/cases/dsh-web-ui-issue-71/fixed/node_modules/@linxin666/dsh-web-ui-all/package.json +5 -0
  58. package/examples/cases/dsh-web-ui-issue-71/fixed/package.json +12 -0
  59. package/examples/cases/dsh-web-ui-issue-71/fixed/pnpm-lock.yaml +18 -0
  60. package/examples/cases/dsh-web-ui-issue-71/fixed/pnpm-workspace.yaml +8 -0
  61. package/examples/cases/dsh-web-ui-issue-71/manual-add/cordis.patch.yml +4 -0
  62. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-client-ui-skin-center/cordis.patch.yml +1 -0
  63. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-client-ui-skin-center/package.json +5 -0
  64. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-client-ui-skin-qq98/cordis.patch.yml +3 -0
  65. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-client-ui-skin-qq98/package.json +5 -0
  66. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-web-ui-all/cordis.patch.yml +1 -0
  67. package/examples/cases/dsh-web-ui-issue-71/manual-add/node_modules/@linxin666/dsh-web-ui-all/package.json +5 -0
  68. package/examples/cases/dsh-web-ui-issue-71/manual-add/package.json +13 -0
  69. package/examples/cases/dsh-web-ui-issue-71/manual-add/pnpm-lock.yaml +21 -0
  70. package/examples/cases/dsh-web-ui-issue-71/manual-add/pnpm-workspace.yaml +5 -0
  71. package/package.json +2 -1
package/README.md CHANGED
@@ -1,1083 +1,123 @@
1
- <h1 align="center">Upstream Radar</h1>
1
+ # Upstream Radar
2
2
 
3
- <p align="center"><strong>Always-on dependency radar for DeepSeek Harness plugins: exact paths, CISA KEV/EPSS priority signals, breaking-change detection, and project-aware Agent follow-up.</strong></p>
3
+ [![CI](https://github.com/MicroMilo/upstream-radar/actions/workflows/ci.yml/badge.svg)](https://github.com/MicroMilo/upstream-radar/actions)
4
+ [![npm](https://img.shields.io/npm/v/upstream-radar)](https://www.npmjs.com/package/upstream-radar)
5
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
4
6
 
5
- <p align="center">
6
- English · <a href="docs/README.zh-CN.md">简体中文</a>
7
- </p>
7
+ **Dependency evidence and upstream-change monitoring for [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) plugins.**
8
8
 
9
- <p align="center">
10
- <a href="https://www.npmjs.com/package/upstream-radar"><img alt="npm version" src="https://img.shields.io/npm/v/upstream-radar?style=flat-square&color=2563eb"></a>
11
- <a href="https://github.com/MicroMilo/upstream-radar/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/MicroMilo/upstream-radar?style=flat-square&color=f59e0b"></a>
12
- <a href="https://github.com/MicroMilo/upstream-radar/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/MicroMilo/upstream-radar/ci.yml?branch=main&style=flat-square&label=CI"></a>
13
- <a href="examples/dsh/README.md"><img alt="Tested with DSH 0.1.0-rc.6" src="https://img.shields.io/badge/tested_with_DSH-0.1.0--rc.6-5b5bd6?style=flat-square"></a>
14
- <a href="https://github.com/MicroMilo/upstream-radar/releases"><img alt="GitHub release" src="https://img.shields.io/github/v/release/MicroMilo/upstream-radar?style=flat-square"></a>
15
- <a href="LICENSE"><img alt="Apache-2.0 license" src="https://img.shields.io/badge/license-Apache--2.0-0f766e?style=flat-square"></a>
16
- </p>
9
+ Upstream Radar answers a practical question before a plugin enters a DSH profile:
17
10
 
18
- <p align="center">
19
- <picture>
20
- <source media="(max-width: 600px)" srcset="docs/assets/upstream-radar-hero-mobile.jpg">
21
- <img src="docs/assets/upstream-radar-hero.jpg" alt="Upstream Radar watches a dependency graph, highlights one affected path, and routes one signal to a DSH Agent." width="100%">
22
- </picture>
23
- </p>
11
+ > Which exact dependency versions does this plugin bring in, which vulnerability or upstream release changed them, and which other plugins will be affected?
24
12
 
25
- <p align="center"><em>Upstream signal exact installed path durable incident project-specific DSH Agent analysis</em></p>
26
-
27
- <p align="center">
28
- <a href="#try-it-in-60-seconds">Try it in 60 seconds</a> ·
29
- <a href="#see-one-incident">See one incident</a> ·
30
- <a href="#install-in-dsh">Install in DSH</a> ·
31
- <a href="#notify-feishu-or-an-https-endpoint">Notify Feishu</a> ·
32
- <a href="#observe-dsh-plugin-upstream-changes">Observe upstream changes</a> ·
33
- <a href="#run-the-proof">Run the proof</a> ·
34
- <a href="#run-it-in-github-actions">Run in GitHub Actions</a> ·
35
- <a href="https://github.com/MicroMilo/upstream-radar/issues/new?template=trial.yml">Share feedback</a> ·
36
- <a href="#how-the-loop-works">How it works</a> ·
37
- <a href="ROADMAP.md">Roadmap</a>
38
- </p>
39
-
40
- ## Start without guessing
41
-
42
- If you are not sure whether this project should use a DSH profile, a lockfile, or the packaged demo, run the read-only guide first:
43
-
44
- ```bash
45
- npx --yes upstream-radar@latest quickstart
46
- ```
47
-
48
- It looks only at the current directory and local DSH profile metadata. It recommends one of the real paths below, labels every suggested command as read-only, local-file creation, or install/start, and refuses to choose between two lockfiles or multiple DSH profiles. The guide itself never installs packages, starts DSH, queries vulnerability sources, or executes plugin code. Use `--json` to feed the result into a setup page or internal launcher.
49
-
50
- ## Choose the smallest path
51
-
52
- | Your goal | Start here | What you get |
53
- | --- | --- | --- |
54
- | Keep a live DSH Agent informed | [`setup`](#install-in-dsh) | A profile-aware monitor that refreshes the installed graph and routes only changed incidents to the matching Agent. |
55
- | Observe DSH plugin upstream changes | [`observe`](#observe-dsh-plugin-upstream-changes) | A scheduled old → new comparison of GitHub commits, npm releases, manifests, and optional lockfile graphs; only meaningful changes wake an Agent. |
56
- | Start monitoring one public plugin in 30 seconds | [Minimal GitHub Actions workflow](examples/github-actions/upstream-observer-minimal.yml) | Copy one file, change the repository URL, and get a daily report without installing or building Radar. |
57
- | Respond to the first alert | [`radar next`](#install-in-dsh) | One read-only command selects the highest-priority incident and points to the DSH task, verified analysis, or next check. |
58
- | Check a DSH profile before starting it | [`profile-check`](#check-a-dsh-profile-before-starting-it) | Reads the actual lockfile and patch rows and blocks missing loader packages, duplicate loader ids, and release-age rollback risk. |
59
- | Add a scheduled CI gate | [GitHub Actions example](examples/github-actions/upstream-radar.yml) | A frozen check from a reviewed config or one lockfile, with a concise Job Summary and a machine-readable JSON report. |
60
- | Check a plugin before installing it | [`graph` / `init` for npm or pnpm lockfiles](#inspect-an-npm-or-pnpm-lockfile-before-installation) | Exact dependency paths and OSV/GitHub Advisory results without running the plugin or its lifecycle scripts. |
61
- | Scan a public DSH plugin repository | `scan https://github.com/owner/repository` | One command shallow-clones the public repo into a temporary directory and reports concrete problems without installing or running it. |
62
- | Review one exact published artifact | `upstream-radar inspect <package>@<exact-version> --deep` | Package, dependency, vulnerability, and provenance evidence for one release. |
63
- | Publish and maintain a DSH plugin | [Plugin author path](#for-dsh-plugin-authors) | Start from a real DSH scaffold, review its locked graph, and add a two-step CI gate before users install it. |
64
- | Send changed events to Feishu | [Feishu or HTTPS webhook](#notify-feishu-or-an-https-endpoint) | Native Feishu V2 text, environment-only secrets, durable acknowledgement, and retry. |
65
-
66
- If you want project-specific reasoning from DSH, use the first path. If you only need an independent admission or regression gate, use the second or third; they do not require a running DSH profile.
13
+ It reads source trees, lockfiles, exact npm artifacts, saved DSH reports, and public advisory feeds. It does not install a plugin or execute its business code during static review.
67
14
 
68
15
  ## Try it in 60 seconds
69
16
 
70
- Want to see the core value before touching DSH? Run the packaged, network-free demo:
71
-
72
17
  ```bash
73
- npx --yes upstream-radar@latest demo
74
- ```
75
-
76
- It prints one exact transitive path, independent advisory-source evidence (including an explicit source conflict), CISA KEV/EPSS prioritization evidence, the read-only DSH Agent handoff, and the next setup command. It uses a local fixture only; it does not inspect your repository, install a plugin, or claim that the demo advisory is real. Use `--json` when you want the same proof as a machine-readable artifact.
77
-
78
- The core result looks like this (the demo uses a local fixture; fields are abbreviated):
79
-
80
- ```text
81
- [HIGH][NEW] Dependency vulnerability
82
- Affected: parser@2.9.0
83
- Paths:
84
- demo-plugin@1.0.0 -> logger@4.0.2 -> parser@2.9.0
85
- Threat signal: CISA KEV lists this CVE as exploited in the wild.
86
- FIRST EPSS estimated exploitation probability: 97.2% (percentile 100.0%)
87
- Next: Review the fixed version with the DSH Agent in this project.
88
- ```
89
-
90
- The useful part is the exact path and project-specific next step—not another generic list of vulnerable package names.
91
-
92
- Want to try it on a real published DSH plugin immediately?
93
-
94
- ```bash
95
- npx --yes upstream-radar@0.33.11 inspect dsh-feishu-bot@0.15.4 --deep
96
- ```
18
+ # No DSH profile, API key, or network state required
19
+ npx --yes upstream-radar@0.34.0 demo
97
20
 
98
- This runs from an otherwise empty directory and returns a short admission,
99
- coverage, dependency-count, vulnerability-count, and next-step summary.
100
-
101
- Want to see a real author-actionable result? This exact published DSH plugin
102
- currently cannot produce a complete dependency graph in a clean npm resolver:
103
-
104
- ```bash
105
- npx --yes upstream-radar@0.33.11 inspect \
106
- @sanqi-normal/dsh-webui-market-plugin@0.5.4 \
107
- --deep --fail-on never
108
- ```
109
-
110
- The result is `review / incomplete`, with the concrete cause
111
- `@deepseek-ai/dsh-compact@^0.0.1-rc.1` not published. No DSH profile, plugin
112
- execution, or LLM is required. See the [reproducible author report](examples/dsh/reports/sanqi-market-plugin-dependency-resolution.md).
113
-
114
- Want to scan a public DSH plugin repository without cloning it yourself? Give
115
- `scan` the GitHub URL:
116
-
117
- ```bash
118
- npx --yes upstream-radar@latest scan \
119
- https://github.com/13071301808/dsh-composer-expand \
21
+ # Scan a public DSH plugin repository without installing it
22
+ npx --yes upstream-radar@0.34.0 scan \
23
+ https://github.com/PlutoKeating/dsh-lark-bot \
120
24
  --fail-on never
121
- ```
122
-
123
- This shallow-clones only the current public branch into a temporary directory,
124
- then reads the source files and lockfile. It does not install dependencies,
125
- run lifecycle scripts, load plugin code, start DSH, or call an LLM. The public
126
- case currently returns `REVIEW / ALLOW / INCOMPLETE` because
127
- `package-lock.json` still says `0.1.0` while `package.json` says `0.1.2`; the
128
- report prints the repair directly: regenerate the lockfile with lifecycle
129
- scripts disabled and review the complete diff. A local `git` executable is the
130
- only extra requirement for this URL form.
131
-
132
- If the repository root has no `package.json`, Radar looks up to three directory
133
- levels deep for one unique package declaring a DSH bundle. For example,
134
- `https://github.com/2008924/dsh-progress-viz` keeps its plugin in `plugin/`; the
135
- same command reports `plugin` as the selected directory and scans it without
136
- installing or running anything. Multiple DSH plugin directories are reported as
137
- ambiguous instead of guessed.
138
-
139
- When the selected directory contains exactly one supported lockfile,
140
- `package-lock.json` or `pnpm-lock.yaml`, the same scan also reconstructs its
141
- committed dependency graph and prints the root, node/edge counts, direct edges,
142
- and unresolved edges. It still does not install dependencies or query an
143
- advisory service; use `inspect --deep` or a Radar check when you need upstream
144
- vulnerability results. If both supported lockfiles are present, the scan stops
145
- and explains the ambiguity instead of choosing one silently.
146
-
147
- Tried the demo or a real DSH setup? [Share a short trial result](https://github.com/MicroMilo/upstream-radar/issues/new?template=trial.yml) with the versions, path, and redacted outcome. Never include source code, secrets, or private paths.
148
-
149
- Every command has its own short guide: `npx --yes upstream-radar@latest setup --help`, `npx --yes upstream-radar@latest inspect --help`, and `npx --yes upstream-radar@latest radar status --help` are useful starting points when you are not sure which path to choose.
150
-
151
- Use DSH with at least one third-party bundle. When it has exactly one such profile, `setup` selects it automatically; pass `--profile <name>` only when you have multiple profiles. The commands below are split between two terminals because DSH normally stays running:
152
-
153
- Before running `setup`, confirm that DeepSeek Harness is installed and `dsh --help` works. If `setup` cannot find `dsh`, it prints this recovery step again.
154
-
155
- If DSH has no third-party plugin profile yet, install the plugin you want to monitor first: `dsh plugin --profile <name> add <package>@<exact-version>`.
156
-
157
- ```bash
158
- # Terminal 1
159
- pnpm dlx --package=upstream-radar@latest upstream-radar setup \
160
- --project-name "My DSH project"
161
- # Use the profile name printed by setup; `web` is only an example.
162
- dsh --profile web --patch ./upstream-radar.dsh.yml
163
- ```
164
-
165
- If you explicitly want setup to launch DSH in the same invocation after its local doctor check passes, add `--start`:
166
25
 
167
- ```bash
168
- pnpm dlx --package=upstream-radar@latest upstream-radar setup \
169
- --project-name "My DSH project" --start
170
- ```
171
-
172
- Without `--start`, setup never launches DSH and gives you a pause to review the generated files. The explicit flag is the one-command path; doctor verifies local wiring, but it is not a human review or a package-safety certificate.
173
-
174
- The one-command path is also covered by a network-free showcase: `pnpm run showcase:setup-start`.
175
-
176
- If you use npm rather than pnpm, the equivalent launcher is `npx --yes upstream-radar@latest setup --project-name "My DSH project"`. For a reproducible team workflow, replace `latest` with the exact release you have reviewed.
177
-
178
- `setup` explicitly installs the exact Radar version used by the command into the selected DSH profile, discovers the installed graph, writes `./upstream-radar.config.json` and `./upstream-radar.dsh.yml` by default, and runs the network-free wiring check. By default it does not start DSH or execute plugin business actions; `--start` opts into launching DSH only after the doctor check passes. Use `--output` or `--dsh-patch` for different paths; if Radar is already installed, add `--no-install`.
179
-
180
- The printed doctor command uses `npx --yes` with the exact same Radar version, so the handoff also works when the first command was launched with npm rather than pnpm.
181
-
182
- After DSH is running, use a second terminal for the read-only status check:
183
-
184
- ```bash
185
- # Terminal 2
186
- pnpm dlx --package=upstream-radar@latest upstream-radar radar status ./upstream-radar.config.json
26
+ # Review the exact package users would install, then check two DSH releases
27
+ npx --yes upstream-radar@0.34.0 review dsh-plugin dsh-feishu-bot@0.15.8 \
28
+ --dsh-version 0.1.0-rc.6,0.1.0-rc.7
187
29
  ```
188
30
 
189
- The setup command writes a reviewable inventory and an explicit DSH overlay. Its local doctor check verifies the wiring before DSH starts; `radar status` confirms the first completed check without another network request. Read the [full DSH setup](#install-in-dsh) for the legacy environment-variable path, profile boundaries, and the real runtime proof.
31
+ The important output is evidence, not a green badge: exact package identity, dependency paths, unresolved edges, install-time scripts, npm integrity/signature/provenance, advisory matches, and DSH load results.
190
32
 
191
- The same state file keeps a bounded audit trail of real transitions. To answer “what changed and when?” without polling any source:
33
+ ## What we have already found
192
34
 
193
- ```bash
194
- pnpm dlx --package=upstream-radar@latest upstream-radar radar history ./upstream-radar.config.json
195
- ```
196
-
197
- It shows `new`, `updated`, `resolved`, and source-health transitions, including the exact affected path. Use `--json` for a dashboard or relay; the ledger keeps the latest 1,000 transitions and deduplicates stable event ids.
198
-
199
- If you want to try the monitoring loop without booting a DSH profile, run one cycle from a reviewed inventory:
200
-
201
- ```bash
202
- pnpm dlx --package=upstream-radar@latest upstream-radar radar watch ./upstream-radar.config.json --once
203
- ```
204
-
205
- Remove `--once` to keep a local monitor alive. This is a lightweight CLI surface for demos, CI, and diagnosis; the native DSH bundle remains the recommended always-on path because it can deliver the task to a live Agent.
206
-
207
- ## Check a DSH profile before starting it
208
-
209
- When the concern is “will this profile boot with the packages and patch rows it
210
- actually has?”, use the static profile check first:
211
-
212
- ```bash
213
- pnpm run build
214
- node dist/src/cli.js profile-check "$DSH_HOME/profiles/web" \
215
- --report ./dsh-profile-check.md
216
- ```
35
+ These are real, reproducible cases in this repository—not synthetic “vulnerable package” demos.
217
36
 
218
- For the shortest answer, add `--summary`:
219
-
220
- ```bash
221
- pnpm dlx --package=upstream-radar@latest upstream-radar profile-check \
222
- "$DSH_HOME/profiles/web" --summary
223
- ```
224
-
225
- When `DSH_HOME` contains exactly one profile with third-party bundles, the
226
- directory can be omitted:
227
-
228
- ```bash
229
- npx --yes upstream-radar@latest profile-check --summary
230
- ```
231
-
232
- With no eligible profile, or more than one, Radar prints the names it found and
233
- asks for an explicit directory; it never guesses between multiple profiles.
234
-
235
- It prints only the status, the important evidence, the reason, and the next
236
- repair. The exit code remains `2` for a blocked profile and `0` for a pass.
237
-
238
- It reads the profile manifest, pnpm/npm lockfile, package metadata,
239
- `pnpm-workspace.yaml`, and `cordis.patch.yml`. It catches the two concrete
240
- failure shapes from [dsh-web-ui #71](https://github.com/zhu1090093659/dsh-web-ui/issues/71)
241
- and [#35](https://github.com/zhu1090093659/dsh-web-ui/issues/35): a loader row
242
- that names a package absent from the locked profile, and the same loader id
243
- being inserted twice. It also points out a pnpm `minimumReleaseAge` policy that
244
- does not exempt the plugin, because that can keep a newly fixed plugin on an
245
- older release during its cooling window.
246
-
247
- This is deliberately a pre-start check: no network, installation, plugin code,
248
- DSH process, Agent, or LLM is involved. A blocked result exits with code `2`.
249
- The complete replay is `pnpm run showcase:dsh-profile-check`; it runs the
250
- public case before the fix, after the manual package workaround, and after the
251
- correct bundled-carrier fix.
252
-
253
- The short, author-facing result is `pnpm run showcase:dsh-case`. It turns the
254
- same three static checks into one repair story: the old profile is blocked,
255
- manually adding the missing package creates a duplicate loader, and the
256
- bundled-carrier update reaches `pass`. If an OpenAI-compatible `issue-locator`
257
- model is available, pass its env file with
258
- `ISSUE_LOCATOR_ENV_FILE=/path/to/issue-locator/.env`; the model only explains
259
- the already-checked facts. If the endpoint is unavailable, the command still
260
- prints the deterministic evidence explanation and records that the fallback was
261
- used. Add `:report` to write the [case analysis result](examples/dsh/reports/dsh-web-ui-issue-71-analysis.json).
262
-
263
- We also ran the current static checks against the first 50 entries in the DSH
264
- plugin registry. That batch found **0 confirmed runtime dependency
265
- vulnerabilities**. It did find real monitoring-quality problems—development-only
266
- dependencies mixed into source lockfiles, three plugin lockfiles whose root
267
- version lagged the source manifest, and a tarball format the scanner could not
268
- parse. The [batch report](examples/dsh/reports/dsh-batch-50-2026-08-17.md)
269
- keeps those results honest; it is not marketed as a vulnerability hit list.
270
- The three repeated lockfile-metadata cases are rechecked in the
271
- [follow-up report](examples/dsh/reports/lockfile-metadata-follow-up-2026-08-18.md),
272
- with an author-ready issue draft for the first maintainer.
37
+ | Case | Finding | Why it matters |
38
+ | --- | --- | --- |
39
+ | [50-plugin batch](examples/dsh/reports/dsh-batch-50-2026-08-17.md) | 0 confirmed runtime dependency vulnerabilities; 3 lockfile root-version mismatches | Monitoring can be wrong even when the vulnerability count is zero. |
40
+ | [`dsh-feishu-bot@0.15.8`](examples/dsh/reports/dsh-feishu-bot-0.15.8-review-2026-08-18.md) | 89-package graph, 12 unresolved optional edges, reachable `protobufjs` `postinstall`, DSH rc.6/rc.7 compatible | “No known CVE” is not the same as “no installation trust boundary.” |
41
+ | [DSH-TUI source vs npm](examples/dsh/reports/dsh-tui-source-vs-npm-2026-08-18.md) | Source has `prepare`; published artifact does not | Source-only and artifact-only reviews answer different questions. |
42
+ | [dsh-composer-expand](examples/dsh/reports/dsh-composer-expand-lockfile-feedback.md) | Committed lockfile root says `0.1.0` while source says `0.1.2` | A small author-fix can restore the identity of the monitored graph. |
273
43
 
274
- ## Observe DSH plugin upstream changes
44
+ We report a confirmed vulnerability only when the affected exact version and runtime path are supported by the available evidence. Development-only hits, missing data, and advisory-source outages remain visibly different states.
275
45
 
276
- This is the upstream-change loop: instead of polling every vulnerability source on
277
- every run, Radar remembers one observation point per plugin and asks what changed
278
- since then.
46
+ ## The core workflow
279
47
 
280
48
  ```text
281
- targets.yml
282
-
283
- GitHub commit + npm package metadata + auto-detected or explicit lockfile
284
-
285
- observations.json
286
-
287
- old new comparison
288
-
289
- only meaningful changes DSH Agent task → report
290
- ```
291
-
292
- Start from the [copyable target example](examples/upstream-observer/targets.yml):
293
-
294
- ```yaml
295
- schema: upstream-radar.observer-targets/v1alpha1
296
- targets:
297
- - id: my-dsh-plugin
298
- ecosystem: dsh
299
- repository: acme/my-dsh-plugin
300
- ref: main
301
- package: my-dsh-plugin
302
- packagePath: plugin/package.json
303
- lockfile: plugin/pnpm-lock.yaml
304
- lockfileType: pnpm
305
- ```
306
-
307
- For one public repository, skip YAML and pass its GitHub URL directly. The
308
- first run creates a baseline; later runs compare the ref you provide. The
309
- observer automatically looks for `pnpm-lock.yaml` or `package-lock.json` and
310
- uses the real committed graph. Add `--package-path` for a nested plugin,
311
- `--package` when the npm name differs from the source manifest, or
312
- `--lockfile` when you want to choose a particular lockfile:
313
-
314
- ```bash
315
- npx --yes upstream-radar@0.33.11 observe \
316
- https://github.com/PlutoKeating/dsh-lark-bot \
317
- --state ./observations.json --report ./upstream-radar-observer.md
318
- ```
319
-
320
- The public example below supplies `--package` because this repository's source
321
- manifest is `dsh-lark-bot`, while its published npm package is
322
- `dsh-feishu-bot`:
323
-
324
- ```bash
325
- npx --yes upstream-radar@0.33.11 observe \
326
- https://github.com/PlutoKeating/dsh-lark-bot \
327
- --package dsh-feishu-bot \
328
- --lockfile pnpm-lock.yaml --lockfile-type pnpm \
329
- --state ./observations.json --report ./upstream-radar-observer.md
330
- ```
331
-
332
- Then run one cycle:
333
-
334
- ```bash
335
- export GITHUB_TOKEN='a read-only GitHub token'
336
- npx --yes upstream-radar@0.33.11 observe \
337
- ./targets.yml \
338
- --state ./observations.json \
339
- --report ./upstream-radar-observer.md
340
- ```
341
-
342
- This uses the published CLI; pin the exact version in CI rather than relying on
343
- `latest`. The first cycle only creates a baseline. Later cycles compare:
344
-
345
- - the source commit and changed files;
346
- - the published npm version and integrity value;
347
- - the package entrypoint, exports, Node requirement, DSH bundle metadata and dependency declarations;
348
- - the real npm or pnpm lockfile graph, when a supported lockfile is committed;
349
- the selected path is recorded in the observation state.
350
-
351
- README/docs/tests-only changes advance the observation point without waking the
352
- Agent. Runtime source, DSH bundle, package entry, dependency graph, npm version,
353
- or npm integrity changes create an old → new task. If the Agent is not configured,
354
- the task stays in `observations.json`; no plugin is installed or executed.
355
-
356
- If you do not have a DSH wrapper configured yet, you can point the observer at
357
- an existing issue-locator/OpenAI-compatible `.env` file instead:
358
-
359
- ```bash
360
- upstream-radar observe ./targets.yml \
361
- --state ./observations.json \
362
- --llm-env-file /path/to/issue-locator/.env
363
- ```
364
-
365
- Radar reads only the endpoint, API key, and model name for that call. It never
366
- writes the key or endpoint into the observation state or report. The model is
367
- called only after a meaningful upstream change; a baseline or docs-only change
368
- does not call it. If the endpoint is unavailable, the deterministic change
369
- record remains pending and can be retried with `--retry-pending`.
370
-
371
- The env file may use the issue-locator names (`ISSUE_LOCATOR_LLM_*`), the common
372
- OpenAI names (`OPENAI_BASE_URL`, `OPENAI_API_KEY`, `OPENAI_MODEL`), or `MODEL` /
373
- `CODEX_MODEL` for the model name.
374
- For ModelBest-style URLs, a 404 on `/llm/v1` also retries `/llm/openai/v1`.
375
-
376
- The scheduled workflow is [examples/github-actions/upstream-observer.yml](examples/github-actions/upstream-observer.yml).
377
- The checked-in workflow is a dogfood workflow for this repository: it checks out
378
- and builds Radar before running the observer. It persists only the observation
379
- point. A quiet run does not create a daily commit.
380
-
381
- For the shortest copy-paste setup, use the [minimal one-repository workflow](examples/github-actions/upstream-observer-minimal.yml).
382
- It uses the published npm CLI, needs no Radar checkout or build, and only asks
383
- you to change the repository URL (or enter one through `workflow_dispatch`).
384
-
385
- The workflow also supports three optional repository secrets—
386
- `ISSUE_LOCATOR_LLM_BASE_URL`, `ISSUE_LOCATOR_LLM_API_KEY`, and
387
- `ISSUE_LOCATOR_LLM_MODEL`. When all three exist, the job sends meaningful tasks
388
- to the issue-locator/OpenAI-compatible model. When they do not exist, static
389
- upstream observation still runs and the job does not pretend that model analysis
390
- was performed.
391
-
392
- ### The DSH Agent boundary
393
-
394
- The observer accepts an explicit executable through `--dsh-agent-command`. It
395
- writes one bounded, read-only task prompt to stdin and expects one JSON conclusion
396
- on stdout. The command is started without a shell, and the prompt treats every
397
- remote repository string and release field as untrusted evidence.
398
-
399
- ```bash
400
- upstream-radar observe ./targets.yml \
401
- --state ./observations.json \
402
- --dsh-agent-command /path/to/reviewed-dsh-agent-wrapper \
403
- --dsh-agent-arg --json
404
- ```
405
-
406
- Radar does not guess an undocumented `dsh` CLI subcommand. A reviewed DSH
407
- headless wrapper is the integration boundary; this keeps the observer usable in
408
- GitHub Actions and lets the DSH adapter evolve without changing observation or
409
- diff logic. Use `--retry-pending` to deliver tasks left by a previous run.
410
-
411
- ## Notify Feishu or an HTTPS endpoint
412
-
413
- To also notify a team-owned HTTPS endpoint when an incident changes, keep the endpoint outside the reviewed config and state:
414
-
415
- ```bash
416
- export UPSTREAM_RADAR_WEBHOOK_URL='https://alerts.example.test/upstream-radar?token=replace-me'
417
-
418
- # Native DSH path: the bundle reads the variable at runtime.
419
- dsh --profile web --patch ./upstream-radar.dsh.yml
420
-
421
- # Or use the CLI path for a persistent one-shot/continuous monitor.
422
- pnpm dlx --package=upstream-radar@latest upstream-radar radar watch \
423
- ./upstream-radar.config.json --webhook "$UPSTREAM_RADAR_WEBHOOK_URL"
424
- ```
425
-
426
- The webhook receives only `new`, `updated`, and `resolved` changes (including source-health changes) in the bounded `upstream-radar.webhook/v1alpha1` JSON format described by the [schema](schemas/webhook.schema.json). A successful HTTP 2xx response records the event id; a failed request remains retryable on the next cycle. The state stores only a SHA-256 endpoint fingerprint, delivery ids, and a bounded copy of events waiting for retry or a quiet window; it never stores the URL or its token. Vulnerability summaries include the same short priority evidence as `radar status`—CISA KEV, then EPSS, then severity—so a Feishu message does not require a second interpretation. For a normal endpoint, this provider-neutral JSON can be turned into a Feishu or Slack card by a relay. For a Feishu/Lark V2 custom bot, Radar recognizes the `/open-apis/bot/v2/hook/` URL and sends the native text body directly:
427
-
428
- ```bash
429
- export UPSTREAM_RADAR_WEBHOOK_URL='https://open.feishu.cn/open-apis/bot/v2/hook/replace-me'
430
- # Only needed when the Feishu bot has signature validation enabled.
431
- export UPSTREAM_RADAR_FEISHU_SECRET='replace-me'
432
-
433
- dsh --profile web --patch ./upstream-radar.dsh.yml
434
- ```
435
-
436
- The Feishu secret is read only from the environment and is never written to the Radar config or state. Follow the [official Feishu custom-bot guide](https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN?lang=zh-CN) when creating the V2 bot. Use the V2 URL; the older `/open-apis/bot/hook/` form is rejected with an actionable error. Run `pnpm run showcase:webhook` to see deduplication and retry behavior without contacting a real endpoint.
437
-
438
- For more than one monitored project, give each project its own environment-variable route. The config stores only the variable names, never the webhook URL or secret:
439
-
440
- ```json
441
- {
442
- "project": {
443
- "id": "payments-api",
444
- "name": "Payments API",
445
- "webhookUrlEnv": "UPSTREAM_RADAR_PAYMENTS_WEBHOOK_URL",
446
- "webhookSecretEnv": "UPSTREAM_RADAR_PAYMENTS_FEISHU_SECRET"
447
- }
448
- }
449
- ```
450
-
451
- ```bash
452
- export UPSTREAM_RADAR_PAYMENTS_WEBHOOK_URL='https://open.feishu.cn/open-apis/bot/v2/hook/replace-me'
453
- export UPSTREAM_RADAR_PAYMENTS_FEISHU_SECRET='replace-me'
454
-
455
- pnpm dlx --package=upstream-radar@latest upstream-radar setup \
456
- --webhook-url-env UPSTREAM_RADAR_PAYMENTS_WEBHOOK_URL \
457
- --webhook-secret-env UPSTREAM_RADAR_PAYMENTS_FEISHU_SECRET
49
+ DSH plugin source / npm artifact
50
+
51
+ exact dependency graph + DSH compatibility evidence
52
+
53
+ saved observation point
54
+
55
+ upstream commit, package, or advisory changes
56
+
57
+ affected-plugin paths and author-facing next action
58
+
59
+ optional DSH Agent analysis only when a meaningful change exists
458
60
  ```
459
61
 
460
- Each project's changed events go only to its configured endpoint. If two projects intentionally share one endpoint, Radar combines them into one delivery target; conflicting Feishu secrets fail the local `doctor` check instead of choosing one silently. The existing global `UPSTREAM_RADAR_WEBHOOK_URL` and CLI `--webhook` paths remain broadcast-compatible for a single team endpoint. Project webhook ledgers are kept separately under endpoint fingerprints, so a failed Payments delivery cannot acknowledge a Platform event.
461
-
462
- ## Control notification noise without losing evidence
463
-
464
- The generated inventory can hold ordinary notices while keeping the full incident, dependency path, history, and DSH task intact. For a first setup, use flags so you do not need to edit JSON by hand:
62
+ For a collection of saved reports, build the reverse index that turns an upstream package update into affected plugins:
465
63
 
466
64
  ```bash
467
- pnpm dlx --package=upstream-radar@latest upstream-radar setup \
468
- --minimum-severity high \
469
- --quiet-hours 'Asia/Shanghai,22:00-08:00'
470
- ```
65
+ npx --yes upstream-radar@0.34.0 graph reverse ./reports \
66
+ --output reverse-dependency-index.json
471
67
 
472
- `init --profile`, `init --pnpm-lock`, and `init --npm-lock` accept the same two flags. `--minimum-severity` accepts `info`, `low`, `medium`, `high`, or `critical`. `--quiet-hours` uses `<IANA timezone>,<HH:MM>-<HH:MM>`; the window may cross midnight. The equivalent generated block is:
473
-
474
- ```json
475
- {
476
- "notificationPolicy": {
477
- "minimumSeverity": "high",
478
- "quietHours": {
479
- "timezone": "Asia/Shanghai",
480
- "start": "22:00",
481
- "end": "08:00"
482
- }
483
- }
484
- }
485
- ```
486
-
487
- `minimumSeverity` applies to vulnerability notices; `critical` vulnerabilities and malicious-package alerts always pass. `quietHours` uses the configured IANA timezone and also supports a window crossing midnight. Compatibility and source-health notices follow the quiet window but are not hidden by a vulnerability severity threshold. With a policy in effect, DSH tasks stay in the durable outbox until they can be delivered, and the webhook outbox keeps pending events for retry or a later policy change. `radar status` shows how many tasks are currently held. Omitting the block keeps the current behavior and delivers every notice. Run `pnpm run showcase:notifications` for a network-free proof of the hold, later delivery, and durable webhook outbox.
488
-
489
- If one active incident is noisy, mute only that incident for a bounded period:
490
-
491
- ```bash
492
- upstream-radar radar next ./upstream-radar.config.json
493
- upstream-radar mute './upstream-radar.config.json.state.json' '<incident-id>' \
494
- --until '2026-08-17T12:00:00Z'
68
+ # Ask: which plugins currently depend on this exact package?
69
+ npx --yes upstream-radar@0.34.0 graph reverse ./reports \
70
+ --package parser@2.9.0
495
71
  ```
496
72
 
497
- This pauses only DSH and webhook delivery. The active incident, exact dependency path, history, and status remain visible; the mute expires automatically, and `radar next` prints the matching `unmute` command. A later event version is delivered again, so muting an old fact cannot hide a new fact. Critical and malware incidents require an explicit `--force`.
498
-
499
- Record the human handoff next to the same incident:
500
-
501
- ```bash
502
- upstream-radar triage './upstream-radar.config.json.state.json' '<incident-id>' \
503
- --status in-progress --owner security-team \
504
- --note 'Trace the parser input path' \
505
- --due '2026-08-17T12:00:00Z'
506
- ```
507
-
508
- The available states are `open`, `in-progress`, `blocked`, and `accepted-risk`. `blocked` and `accepted-risk` require a note. `--due` is an optional human deadline; `radar status` and `radar next` mark it as overdue when it passes. This is workflow context only: it never marks an active vulnerability resolved or suppresses its evidence. The record is bound to the exact event id, so an updated upstream fact requires a fresh review; `radar status` and `radar next` show the current owner, note, and deadline.
509
-
510
- ---
511
-
512
- A vulnerability feed stops at “package X is affected.” Upstream Radar keeps going: it identifies the exact installed dependency path, maintains one durable incident, and wakes a [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Agent with the project evidence needed for a useful investigation.
73
+ The generated JSON preserves exact paths such as:
513
74
 
514
75
  ```text
515
- OSV/GitHub Advisory or npm release
516
- -> exact installed plugin path
517
- -> new / updated / resolved incident
518
- -> project-specific DSH Agent analysis task
519
- ```
520
-
521
- **No matching installed path means no Agent wake-up.** Version matching and compatibility facts are calculated by code; the model handles only repository-specific judgment.
522
-
523
- Radar checks OSV and the GitHub Advisory Database as independent vulnerability sources. If both sources describe the same issue through a GHSA or CVE alias, Radar emits one incident and keeps the source identifiers, source list, and fix versions together; human-readable output says `Sources: OSV + GitHub Advisory Database` for this cross-confirmed case. If their severity or fixed-version claims differ, the event also says `Source conflict` and shows each claim instead of silently making the operator infer why multiple fixes are listed. If one source times out, the last confirmed vulnerability evidence and incident identity are retained; the source itself becomes a visible health incident after three consecutive failures instead of being treated as clean. The CLI and DSH adapter use the GitHub source by default, accept an optional `GITHUB_TOKEN` from the environment for API rate limits, and expose `--no-github-advisories` when an operator deliberately needs an OSV-only run. See the [GitHub Advisory Database API](https://docs.github.com/en/rest/security-advisories/global-advisories?apiVersion=2026-03-10) for the upstream query contract.
524
-
525
- ## The missing middle: candidate dependency graphs
526
-
527
- An upgrade can look clean at the top level while introducing a vulnerable transitive package. Radar therefore does not stop at `plugin@1.3.0`'s manifest:
528
-
529
- ```text
530
- candidate plugin@1.1.0
531
- └── logger@4.1.0
532
- └── parser@2.9.0 ← OSV advisory
533
- ```
534
-
535
- For the earliest bounded set of newer versions, Radar resolves npm metadata into a temporary `package-lock.json` with lifecycle scripts disabled, queries every resolved node against OSV, and keeps the exact path in the compatibility event. A missing required edge, resolver failure, or OSV failure is shown as incomplete or unavailable; it is never presented as “no vulnerability found”. Later versions are marked as unchecked when the candidate list is larger than the bounded prefix. The result is still a starting point for DSH project analysis, not an upgrade certificate.
536
-
537
- ## Inspect an npm or pnpm lockfile before installation
538
-
539
- If a DSH plugin is managed with pnpm, inspect the exact locked tree before putting it into a DSH profile:
540
-
541
- ```bash
542
- pnpm dlx --package=upstream-radar@latest upstream-radar graph pnpm-lock \
543
- ./pnpm-lock.yaml \
544
- --json
545
- ```
546
-
547
- This reads only the lockfile. It does not run `pnpm install`, lifecycle scripts, plugin code, or network requests. It understands pnpm v6/v9 package locators, peer-context variants, duplicate versions, and a project root declared through the `importers` section. An unresolved or ambiguous dependency remains visible instead of being guessed away. The JSON is the same canonical graph shape used by Radar's OSV path matching, so a CI job can review the graph before the plugin is admitted to DSH. Run `pnpm run showcase:pnpm-lock` for a real repository example.
548
-
549
- To turn that graph into a monitorable inventory and run the first vulnerability check:
550
-
551
- ```bash
552
- pnpm dlx --package=upstream-radar@latest upstream-radar init \
553
- --pnpm-lock ./pnpm-lock.yaml \
554
- --project-name "My DSH plugin"
555
-
556
- pnpm dlx --package=upstream-radar@latest upstream-radar radar check \
557
- ./upstream-radar.config.json --frozen --fail-on high
558
- ```
559
-
560
- `init --pnpm-lock` does not need a DSH profile and writes a normal Radar config; `radar check` then queries exact locked versions and emits the same DSH-ready event shape. Use the native DSH `setup` path when the plugin is installed and should receive follow-up analysis in a live Agent. Run `pnpm run showcase:pnpm-lock:monitor` to see the complete lockfile-to-OSV event locally.
561
-
562
- When `package.json` sits beside `pnpm-lock.yaml`, `--root` can be omitted; Radar reads the exact package name and version from that manifest. Keep `--root` when the lockfile belongs to another workspace root or when you want the admission coordinate to be explicit.
563
-
564
- The same path works for npm projects with a committed `package-lock.json`:
565
-
566
- ```bash
567
- pnpm dlx --package=upstream-radar@latest upstream-radar graph npm-lock \
568
- ./package-lock.json --json
569
-
570
- pnpm dlx --package=upstream-radar@latest upstream-radar init \
571
- --npm-lock ./package-lock.json \
572
- --project-name "My DSH plugin"
76
+ plugin@1.0.0 logger@4.0.2 parser@2.9.0
573
77
  ```
574
78
 
575
- For an npm project root, Radar reads `packages[""]` from the lockfile and ignores the root package's development-only dependencies. The command still does not install packages, run lifecycle scripts, load plugin code, or make network requests until the subsequent `radar check` queries OSV.
79
+ It also preserves whether the graph is complete or has unresolved optional/peer edges. A later website can visualize this index; the index and evidence remain the product foundation.
576
80
 
577
- Run `pnpm run showcase:npm-lock:monitor` for a deterministic local proof of this npm lockfile-to-OSV-to-DSH event path.
81
+ ## GitHub Action
578
82
 
579
- ## For DSH plugin authors
580
-
581
- If you start with the real [`create-dsh-plugin`](https://www.npmjs.com/package/create-dsh-plugin) scaffold, the shortest review-first path is:
582
-
583
- ```bash
584
- npx create-dsh-plugin my-dsh-plugin -t tool --yes --skip-install
585
- cd my-dsh-plugin
586
- pnpm install --ignore-scripts
587
-
588
- # Read the exact graph before adding the plugin to a DSH profile.
589
- pnpm dlx --package=upstream-radar@0.33.11 upstream-radar graph pnpm-lock pnpm-lock.yaml --json
590
- ```
591
-
592
- The graph includes the exact DSH package versions and keeps unresolved optional peers visible. It does not load the generated plugin or run lifecycle scripts. After reviewing it, copy this complete workflow into `.github/workflows/upstream-radar.yml`:
83
+ The repository already contains a reusable, composite Action in [`action.yml`](action.yml). It runs the same frozen Radar check in CI and writes a short Job Summary.
593
84
 
594
85
  ```yaml
595
- name: Upstream Radar
596
-
597
- on:
598
- workflow_dispatch:
599
- pull_request:
600
- schedule:
601
- - cron: '17 6 * * *'
602
-
603
- permissions:
604
- contents: read
605
-
606
- jobs:
607
- dependency-radar:
608
- runs-on: ubuntu-latest
609
- steps:
610
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
611
- - uses: MicroMilo/upstream-radar@v0.33.11
612
- with:
613
- fail-on: high
614
- fail-on-compatibility: breaking
615
- ```
616
-
617
- The Action auto-detects the one `pnpm-lock.yaml`, checks the same exact graph, and writes the result to the Job Summary. This is a pre-install and CI gate; it does not install the plugin into DSH. After the graph is reviewed, use the normal `dsh plugin` flow to install it and `upstream-radar setup` to start project-aware monitoring.
618
-
619
- To check a real published DSH artifact directly, run one command:
620
-
621
- ```bash
622
- npx --yes upstream-radar@0.33.11 inspect dsh-feishu-bot@0.15.4 --deep
623
- ```
624
-
625
- The checked result is `REVIEW`: registry integrity, signature, provenance, and
626
- 89 resolved packages are verified; known vulnerabilities are `0`, while 12
627
- optional dependency edges remain unresolved. That is a useful author result:
628
- the empty vulnerability list is visible, but it is not mislabeled as a full
629
- `ALLOW` decision.
630
-
631
- The same exact tarball loads in both DSH `0.1.0-rc.6` and `0.1.0-rc.7` disposable
632
- profiles. See the [real compatibility probe](examples/dsh/reports/dsh-feishu-bot-0.15.4-probe.md)
633
- for the command and boundary.
634
-
635
- ## See one incident
636
-
637
- If an advisory affects only one of two installed `parser` versions, Radar reports the path that actually matched:
638
-
639
- ```text
640
- [HIGH][NEW] Dependency vulnerability
641
- Project: Payments API (payments-api)
642
- Plugin: plugin@1.0.0
643
- Affected: parser@2.9.0
644
- Origin: plugin profile
645
- Advisory: GHSA-demo-2026-parser / CVE-2026-1234
646
- Sources: OSV + GitHub Advisory Database
647
- Source conflict: fixed versions — OSV=3.0.0; GitHub Advisory Database=3.1.0
648
- Paths:
649
- plugin@1.0.0 -> logger@4.0.2 -> parser@2.9.0
650
- Fixed versions: 3.0.0, 3.1.0
651
- Route: payments-platform via feishu:payments-security
652
- ```
653
-
654
- That incident becomes a plugin-originated DSH notice. Radar keeps both source claims visible instead of silently picking one fixed version; the DSH Agent then decides which fix is appropriate for the project. It is not copied into a generic chatbot prompt.
655
-
656
- For a CVE, native DSH also adds two prioritization signals:
657
-
658
- ```text
659
- Threat signal: CISA KEV lists this CVE as exploited in the wild.
660
- FIRST EPSS estimated exploitation probability: 97.2% (percentile 100.0%)
661
- ```
662
-
663
- [CISA KEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) answers “is this CVE known to be exploited in the wild?” [FIRST EPSS](https://www.first.org/epss/) gives a daily estimate of exploitation probability and relative percentile. They help the Agent and the team decide what to inspect first; they do not change the exact dependency match, and a missing signal is not a safety certificate.
664
-
665
- To replay the two signals and a source outage without network access:
666
-
667
- ```bash
668
- pnpm run showcase:threat-intel
669
- ```
670
-
671
- | Upstream signal | Radar proves deterministically | DSH Agent investigates |
672
- | --- | --- | --- |
673
- | Vulnerability or malicious package | affected `name@version`, every installed path, fixed versions, incident state | whether project code reaches it, attacker input can reach it, and the least disruptive fix |
674
- | Candidate npm release | version boundary and Node.js, peer, export, entrypoint, bundle, and dependency changes; exact direct and transitive candidate OSV status; when possible, the first newer version without a deterministic blocker or known vulnerable path | which APIs or Cordis configuration would break and what migration is appropriate; the first candidate is never treated as a safety certificate |
675
-
676
- ## Install in DSH
677
-
678
- Upstream Radar is an npm-published DSH bundle, so no install-time build permission is required:
679
-
680
- ```bash
681
- pnpm dlx --package=upstream-radar@latest upstream-radar setup \
682
- --project-name "My DSH project"
683
- ```
684
-
685
- When exactly one DSH profile contains third-party bundles, `setup` selects it automatically; pass `--profile <name>` when more than one profile is eligible. `setup` delegates the package installation to DSH using the exact Radar version currently being run. It then generates `upstream-radar.config.json` and `upstream-radar.dsh.yml` by default, and runs `doctor` locally without contacting OSV, npm, or GitHub. By default it does not start DSH. The explicit one-command path is:
686
-
687
- ```bash
688
- pnpm dlx --package=upstream-radar@latest upstream-radar setup \
689
- --project-name "My DSH project" --start
690
- ```
691
-
692
- For the review-first path, start the generated overlay yourself:
693
-
694
- ```bash
695
- dsh --profile web --patch ./upstream-radar.dsh.yml --dump-config
696
- dsh --profile web --patch ./upstream-radar.dsh.yml
697
- pnpm dlx --package=upstream-radar@latest upstream-radar radar status ./upstream-radar.config.json
698
- ```
699
-
700
- For an already installed bundle, add `--no-install`. The lower-level manual path remains available when you want to review the DSH installation separately: run `dsh plugin --profile web add upstream-radar@<exact-version>`, then use `init --profile web --dsh-patch ...` and `doctor`.
701
-
702
- The initializer reads the profile's actual third-party bundles and follows the installed `node_modules` tree exposed by that profile, including duplicate versions, overrides, and local package-manager choices. By default it records the workspace as `.` so the config can be committed and reused on another machine; start DSH from the project root. Pass `--workspace <absolute-path>` only when DSH is launched elsewhere. It reads manifests only: it does not import plugin code, run lifecycle scripts, start DSH, or enable polling.
703
-
704
- If startup does not behave as expected, run the local wiring check before looking at upstream feeds:
705
-
706
- ```bash
707
- pnpm dlx --package=upstream-radar@latest upstream-radar doctor ./upstream-radar.config.json \
708
- --profile web \
709
- --patch ./upstream-radar.dsh.yml
710
- ```
711
-
712
- `doctor` does not contact OSV, npm, GitHub, or execute plugin code. It checks that the config parses, the selected DSH profile actually registers `upstream-radar`, the overlay points to the same config and state files, the dependency coverage is complete, and the durable state can be read. When `UPSTREAM_RADAR_WEBHOOK_URL` is set, it also checks the HTTPS route locally, recognizes the native Feishu/Lark V2 path, and blocks the retired V1 path before the first poll; it never prints the URL or `UPSTREAM_RADAR_FEISHU_SECRET`. It exits non-zero only for a blocked setup; a missing first-run state is shown as a warning with the next command to run. Add `--json` when another tool needs the result.
713
-
714
- The generated overlay points DSH at the config and state files explicitly and records the selected profile. If `--registry <url>` was used during initialization, the same registry is carried into the running DSH monitor; otherwise release and candidate checks use the public npm registry. Before each native DSH polling cycle, and before each CLI `radar check` or `radar watch` cycle, it re-reads that profile's installed graph, so later plugin installs, upgrades, removals, and host-runtime changes are not silently missed. If the refresh fails, that cycle stops without replacing the last durable state. `radar status` remains read-only and reports whether a check has completed, which source is unhealthy, whether dependency coverage is complete, the most important active incidents with their exact path or candidate, a suggested next step, pending DSH tasks, and verified model conclusions. Its Attention list is ordered by CISA KEV evidence, then EPSS score, then advisory severity; each vulnerability line shows the evidence that was actually available. Missing signals are not treated as a safety claim. `radar history` is also read-only and shows the bounded transition ledger from the same state file, including resolved incidents that no longer appear in the active summary. You can inspect a stored conclusion with `upstream-radar analysis list <state.json>` or `analysis show <state.json>`. Radar accepts a conclusion only when the response is strict JSON from the matching DSH model session; it never treats arbitrary chat as an analysis result. `radar compare` remains a manual comparison of the files you provide. If you prefer environment variables or need to override the polling interval, omit `--dsh-patch` and use `UPSTREAM_RADAR_CONFIG`, `UPSTREAM_RADAR_STATE`, `UPSTREAM_RADAR_INTERVAL_SECONDS`, `UPSTREAM_RADAR_REGISTRY`, and `UPSTREAM_RADAR_DEEP_CANDIDATES` as before.
715
-
716
- When you only want the next action after an alert, use the shorter read-only view:
717
-
718
- ```bash
719
- pnpm dlx --package=upstream-radar@latest upstream-radar radar next ./upstream-radar.config.json
720
- ```
721
-
722
- It selects the same first incident as `radar status`, then points to its queued DSH task, verified analysis, or the next check command. When a DSH conclusion is already verified, the output also prints its urgency, recommendation, and bounded evidence list so the operator can act without opening a second report. If a task is queued, it also prints the explicit `task ack` command; acknowledging removes only that delivery item, not the active incident or its evidence.
723
-
724
- The generated graph is the actual installed profile graph. During a native DSH run, Radar also reads the exact DSH CLI entrypoint (`@deepseek-ai/dsh/lib/bin.js`) and discovers the `node_modules` plane that process is using. It does this with bounded, read-only manifest checks: it does not import DSH, load a plugin, or run an install hook. Packages resolved from that plane are marked as `dsh-host`, and their exact versions are checked for advisories. Radar also records the exact `@deepseek-ai/dsh` executable package that owns the plane, so the DSH core and its reachable host dependencies get OSV and npm release checks even when they are not declared plugin dependencies. The graph uses an explicit `host-runtime` boundary edge; a host finding is never presented as an ordinary plugin dependency. `radar status` says whether the host plane came from the running DSH process or the profile fallback. If a required dependency is declared but cannot be resolved from either place, it remains visible as incomplete coverage instead of being treated as absent. Missing optional platform packages are retained as evidence but do not make coverage incomplete. Passing `--registry <url>` explicitly selects the older public npm artifact graph path, which is useful for comparing a profile against registry resolution but is not the default.
725
-
726
- For a hand-written or CI fixture, use [the example inventory](examples/radar/config.json). If neither a generated `--patch` overlay nor `UPSTREAM_RADAR_CONFIG` is provided, the bundle stays dormant and performs no polling.
727
-
728
- Once running, Radar polls OSV, GitHub Advisory Database, npm, and public GitHub Releases, then queries CISA KEV and FIRST EPSS for matched CVEs. Native DSH enables those two prioritization feeds by default; set `UPSTREAM_RADAR_THREAT_INTEL=false` when a lean run should omit them. The signals do not decide whether a package is vulnerable: they only explain which confirmed incidents deserve attention first. Radar persists incident state before delivery, and submits only changed incidents to the matching DSH project session. With one root Agent, delivery remains automatic; with multiple roots, Radar requires an exact match between `project.workspace` and `Agent.session.header.cwd`, and keeps the task queued when it cannot prove the route. The native adapter records the exact message id, DSH session, task id, and event id; it writes back only a matching `assistant/message` from that session whose visible text is the six-field JSON result. A new or updated upstream event invalidates the previous result, so an old model conclusion cannot survive a changed dependency fact. If a source is temporarily unavailable, Radar keeps the last confirmed state instead of claiming that the project is clean, continues delivering already queued tasks, and creates one source-health notice for that source after three consecutive failures.
729
-
730
- Each release cycle also checks a bounded prefix of candidate dependency graphs. This is enabled by default in the DSH adapter and CLI; use `--no-deep-candidates` only when you deliberately want manifest-only compatibility checks. The graph resolver is isolated in a temporary directory and uses `package-lock-only` plus `ignore-scripts`, so candidate package code is not loaded or executed.
731
-
732
- ## Run the proof
733
-
734
- Boot a real DSH `headless` profile with the packed Upstream Radar bundle installed:
735
-
736
- ```bash
737
- git clone https://github.com/MicroMilo/upstream-radar.git
738
- cd upstream-radar
739
- corepack enable
740
- pnpm install --frozen-lockfile
741
- pnpm run try:dsh
742
- ```
743
-
744
- No DeepSeek API key is required. The paid model endpoint is replaced by a deterministic local DeepSeek-compatible stub; the Cordis loader, DSH Agent, Session, persistence stack, bundle installation, and plugin delivery are real.
745
-
746
- The command fails unless DSH proves all five facts:
747
-
748
- ```json
749
- {
750
- "bundleInstalled": true,
751
- "radarTaskReachedModel": true,
752
- "pluginSourcePreserved": true,
753
- "pendingTasksAfterDelivery": 0,
754
- "analysisResults": 1,
755
- "dshEntrypointObserved": true,
756
- "dshHostRuntimePlaneDiscovered": true
757
- }
758
- ```
759
-
760
- This proof runs in CI on Node.js 22. See the executable [showcase contract](examples/dsh/README.md) and its checked-in [result](examples/dsh/reports/headless-smoke.json). Run `pnpm run try:dsh:live` to include a current OSV and npm poll before the DSH handoff.
761
-
762
- To demonstrate the host-runtime dependency path specifically, run `pnpm run showcase:dsh-runtime`. It starts a real DSH `headless` process, begins at the exact DSH executable package, walks the reachable host dependency closure behind an explicit `host-runtime` boundary, queries a local OSV-compatible feed for the real `@deepseek-ai/cordis` version, persists a `dsh-host` vulnerability path, and hands it to the DSH Agent. The model and advisory are deterministic local stubs; this proves integration and provenance, not the safety of a real advisory.
763
-
764
- To see why one shared host bug should not page every plugin separately, run `pnpm run showcase:dsh-host-alert`. Two plugin roots share the same exact `@deepseek-ai/cordis` version; Radar emits one project event, keeps both exact paths, and creates one DSH analysis task. Add `:report` to refresh the checked-in [deduplication result](examples/dsh/reports/dsh-host-alert-dedup.json).
765
-
766
- To validate the actual first-use path against several real published DSH plugins, run `pnpm run showcase:dsh-adoption`. It creates a disposable `DSH_HOME`, packs exact Radar and plugin tarballs with lifecycle scripts disabled, lets DSH build its own host runtime, runs `setup --no-install`, `doctor`, a frozen OSV/npm/GitHub check, and the human-readable status surface. The checked-in trial covers [`dsh-cloudflare-browser-run@0.1.1`](https://www.npmjs.com/package/dsh-cloudflare-browser-run), [`@open-agfs/dsh-agfs@0.1.9`](https://www.npmjs.com/package/@open-agfs/dsh-agfs), and [`dsh-feishu-bot@0.14.0`](https://www.npmjs.com/package/dsh-feishu-bot). The first two install and become monitorable; the Feishu bridge is intentionally recorded as blocked because a clean DSH profile stops on its transitive `protobufjs` build script until a human approves it. A blocked install is not reported as a clean security result. The showcase does not start a DSH Agent or call a model; the separate `try:dsh` proof covers that handoff. The checked-in [adoption result](examples/dsh/reports/adoption-smoke.json) records each plugin's install state, graph coverage, source health, and boundary.
767
-
768
- The DSH Agent handoff is optional for dependency analysis. If you want to smoke-test that integration with a real published plugin, run `pnpm run try:dsh:real`. It installs the exact published `dsh-find-plugin@0.3.6` into a disposable headless profile and proves that a real DSH Agent receives, consumes, and writes back a Radar analysis task. The model is still a local deterministic stub; no plugin business action or paid endpoint is called. Set `DSH_REAL_PLUGINS` to another exact package only after checking its required profile and credentials.
769
-
770
- For a public compatibility case, run `pnpm run try:dsh:public-case`. It replays [`dsh-web-ui #35`](https://github.com/zhu1090093659/dsh-web-ui/issues/35) and [`#71`](https://github.com/zhu1090093659/dsh-web-ui/issues/71): the old profile is blocked by a missing loader, the manual package workaround is blocked by a duplicate loader id, and the maintainer's bundled-carrier fix passes. The same compatibility event is then admitted to a real DSH `headless` session and written back as one verified `analysisResult`; see the checked-in [case result](examples/dsh/reports/dsh-web-ui-public-case.json). This is an end-to-end delivery proof with a local deterministic model stub, not a claim about online model quality. The command does not use your DSH credentials or call a paid model endpoint.
771
-
772
- To see the two-source vulnerability contract without contacting the network, run `pnpm run showcase:github-advisories`. It feeds the same parser issue through OSV and a deterministic GitHub Advisory Database client, proves that two reports become one Radar incident with explicit source provenance and a visible fixed-version conflict, then simulates three GitHub failures and recovery. The existing vulnerability remains active throughout; only the GitHub source-health incident changes.
773
-
774
- ## Validate the compatibility rules
775
-
776
- Before wiring a project into a compatibility gate, run the offline rule benchmark:
777
-
778
- ```bash
779
- pnpm dlx --package=upstream-radar@0.33.11 upstream-radar benchmark compatibility
780
- ```
781
-
782
- It covers six contracts: a safe patch, a change that only needs project analysis, an incompatible DSH peer, a publisher-declared breaking release, a vulnerable candidate dependency, and an incomplete candidate graph. The command does not access the network, install a package, load a plugin, or start DSH. It checks the behavior of Radar's deterministic rules and the `breaking`/`any` gates; it is not a runtime compatibility proof.
783
-
784
- ## Probe a real DSH bundle
785
-
786
- When you have an exact plugin artifact and want to know whether one exact DSH release can load it, run the bounded probe:
787
-
788
- ```bash
789
- # Pack an exact npm release without running its lifecycle scripts.
790
- npm pack --ignore-scripts dsh-plugin@1.2.3
791
-
792
- pnpm dlx --package=upstream-radar@0.33.11 upstream-radar probe dsh-load \
793
- ./dsh-plugin-1.2.3.tgz \
794
- --dsh-version 0.1.0-rc.6
795
- ```
796
-
797
- The probe reads the tarball first, requires a package-local `dsh.bundle.patch`, and refuses lifecycle scripts. It then creates a temporary DSH `headless` profile, adds the exact tarball, checks that DSH registered the bundle, and runs `--dump-config`. The profile is removed at the end unless `--keep-profile` is supplied.
798
-
799
- There are three deliberate outcomes:
800
-
801
- | Result | Meaning | Exit code |
802
- | --- | --- | ---: |
803
- | `compatible` | This DSH version registered the bundle and loaded its configuration. | `0` |
804
- | `incompatible` | DSH accepted installation but rejected registration or configuration loading. | `2` |
805
- | `unknown` | Preflight, DSH startup, installation, or the time limit prevented a reliable conclusion. | `1` |
806
-
807
- This is a load-compatibility check only. It does not run plugin business actions, test model behavior, or prove that the package and its dependencies are safe. The repository's reproducible three-case demo is:
808
-
809
- ```bash
810
- pnpm run showcase:dsh-probe
811
- ```
812
-
813
- It exercises a loadable bundle, a bundle patch DSH rejects, and a package that remains `unknown` because it declares `postinstall`.
814
-
815
- To compare a plugin against more than one DSH release, use the matrix form:
816
-
817
- ```bash
818
- pnpm dlx --package=upstream-radar@0.33.11 upstream-radar probe dsh-matrix \
819
- ./dsh-plugin-1.2.3.tgz \
820
- --dsh-version 0.1.0-rc.3 \
821
- --dsh-version 0.1.0-rc.6 \
822
- --json
823
- ```
824
-
825
- The matrix runs versions one at a time in separate temporary profiles and evaluates the same artifact each time. It needs at least two distinct exact versions and accepts at most eight. The aggregate is `incompatible` if any version is incompatible, `unknown` if none is incompatible but at least one result is unknown, and `compatible` only when every tested version loads successfully. The JSON shape is documented in the [matrix result schema](schemas/dsh-load-matrix.schema.json).
826
-
827
- ## Run it in GitHub Actions
828
-
829
- If your team wants the shortest scheduled CI gate before wiring a machine to a live DSH profile, copy [the example workflow](examples/github-actions/upstream-radar.yml). It auto-detects the only `pnpm-lock.yaml` or `package-lock.json` after checkout, so no Radar config is required for the first run. If you already maintain a reviewed `upstream-radar.config.json`, pass it explicitly instead. The reusable Action keeps the workflow to two meaningful steps, with an optional third step for DSH load compatibility:
830
-
831
- ```yaml
832
- steps:
833
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
834
- - uses: MicroMilo/upstream-radar@v0.33.11
835
- with:
836
- fail-on: high
837
- # Optional: also fail on deterministic DSH/plugin compatibility breaks.
838
- fail-on-compatibility: breaking
839
- # Optional: add CISA KEV and FIRST EPSS signals to matched CVEs.
840
- threat-intel: true
841
- ```
842
-
843
- The Action is a thin wrapper around `radar check --frozen --state :memory: --fail-on high --json`; when the optional compatibility input is enabled, it also passes `--fail-on-compatibility breaking` or `any`. `--frozen` is deliberate: it uses the graph in the reviewed config and does not try to read a developer's local DSH profile. `threat-intel` is false by default so an ordinary CI gate stays lean; set it to `true` when the Job Summary and raw JSON should include CISA KEV and FIRST EPSS prioritization evidence. Each run is independent, exits `2` when an active vulnerability or opted-in compatibility change meets its threshold, and exits `1` for an operational or source error. `breaking` catches confirmed or strong incompatibility signals; `any` catches every active compatibility event. The default is `never`, so vulnerability-only behavior stays unchanged. In addition to the raw JSON log, the Action writes a short escaped summary to the GitHub Job Summary so a scheduled failure immediately shows the affected package, exact path, published fix version when available, one-line priority evidence, and a suggested next step. The Action does not deliver a DSH Agent task or modify a branch; the native DSH bundle remains the always-on analysis path. Pin the Action to a release tag such as `v0.33.11`, and pin the checkout Action in your workflow according to your repository's policy.
844
-
845
- If the repository has no committed Radar config yet, the smallest setup is to omit `config`, `pnpm-lock`, and `npm-lock`. After checkout, the Action automatically uses the only one of `pnpm-lock.yaml` or `package-lock.json` that exists, generates a temporary reviewed config, and runs the same frozen check:
846
-
847
- ```yaml
848
- - uses: MicroMilo/upstream-radar@v0.33.11
849
- with:
850
- fail-on: high
851
- ```
852
-
853
- An existing `config` wins over auto-detection. If both lockfiles exist, or neither a config nor a supported lockfile exists, the Action stops with a direct message instead of guessing.
854
-
855
- To review the exact plugin artifact before it enters DSH, add `inspect-package`:
856
-
857
- ```yaml
858
- - uses: MicroMilo/upstream-radar@v0.33.11
859
- with:
860
- inspect-package: dsh-cloudflare-browser-run@0.1.1
861
- # review is the safe default; use block only when incomplete coverage may pass.
862
- inspect-fail-on: review
863
- ```
864
-
865
- This downloads that exact npm tarball, verifies the registry integrity/signature and provenance when available, resolves dependencies with lifecycle scripts disabled, and puts the admission verdict, coverage, findings, and next step in the Job Summary. Write the input as `package@version`; the Action adds the internal `npm:` prefix. It does not install or execute the plugin. The optional `inspect-verdict` output exposes `allow`, `warn`, `review`, or `block` to later workflow steps. An empty finding list with incomplete coverage remains a review result, not a safety certificate.
866
-
867
- If the repository has a pnpm lockfile but no committed Radar config yet, the Action can generate the config in the same job. See the [copyable pnpm workflow](examples/github-actions/upstream-radar-pnpm.yml):
868
-
869
- ```yaml
870
- - uses: MicroMilo/upstream-radar@v0.33.11
871
- with:
872
- pnpm-lock: pnpm-lock.yaml
873
- fail-on: high
874
- ```
875
-
876
- This mode runs `init --pnpm-lock` first and then the same frozen check. `root` is optional when `package.json` is beside the lockfile, and can be supplied for an explicit or non-adjacent workspace root. It never installs the project or executes the plugin; `config` is the output path (default `upstream-radar.config.json`). Leave `pnpm-lock` empty to keep the reviewed-config mode above.
877
-
878
- Set `npm-lock: package-lock.json` instead for npm projects; `pnpm-lock` and `npm-lock` are mutually exclusive. Both modes infer the root from the adjacent `package.json` unless `root` is supplied.
879
- See the [copyable npm workflow](examples/github-actions/upstream-radar-npm.yml) for that form.
880
-
881
- The Action requires the caller to check out the repository first. It does not install the project's dependencies or run their lifecycle scripts; it only reads the committed graph and queries the configured upstream sources. For a fully explicit, lower-level invocation, the equivalent command is:
882
-
883
- ```bash
884
- pnpm dlx --package=upstream-radar@0.33.11 upstream-radar radar check \
885
- ./upstream-radar.config.json --frozen --state :memory: --fail-on high \
886
- --fail-on-compatibility breaking --json
887
- ```
888
-
889
- To add the optional DSH load matrix for a published plugin, provide an exact npm package and at least two exact DSH versions:
890
-
891
- ```yaml
892
- - uses: MicroMilo/upstream-radar@v0.33.11
893
- id: radar
86
+ - uses: MicroMilo/upstream-radar@v0.34.0
894
87
  with:
895
88
  config: upstream-radar.config.json
896
89
  fail-on: high
897
- probe-package: dsh-cloudflare-browser-run@0.1.1
898
- probe-dsh-versions: 0.1.0-rc.3,0.1.0-rc.6
899
90
  ```
900
91
 
901
- The Action packs the exact package with `--ignore-scripts`, runs `probe dsh-matrix`, exposes `probe-result`, and fails with the probe's exit code when the result is `incompatible` or `unknown`. This extra step downloads and loads the DSH bundle in temporary profiles; it is a compatibility signal, not a security sandbox or a capability test.
902
-
903
- For a runnable consumer example using the real [`dsh-cloudflare-browser-run@0.1.1`](examples/github-actions/consumer/upstream-radar.config.json) graph, see the [consumer smoke README](examples/github-actions/consumer/README.md) and its [copyable workflow](examples/github-actions/consumer/upstream-radar.yml).
904
-
905
- Run the consumer path locally from this repository with:
906
-
907
- ```bash
908
- pnpm run try:consumer
909
- ```
910
-
911
- This builds the current checkout and runs the same frozen consumer check with the local CLI, so it also works before the next npm version is published. To deliberately verify the public npm artifact instead, run `pnpm run try:consumer:published`; it resolves the package version in `package.json` from npm and should only be used after that version exists publicly.
912
-
913
- For a local or self-hosted DSH machine, omit `--frozen` so Radar refreshes the selected profile before each cycle. Use `--fail-on` or `--fail-on-compatibility` only with `radar check`, `radar status`, or `radar watch --once`; a long-running watch should continue routing incidents instead of terminating on the first one.
92
+ See the [consumer workflow](examples/github-actions/consumer/README.md) for config and lockfile examples. The GitHub Marketplace prompt is a distribution opportunity, not a separate scanning engine: the Action listing should follow a reviewed stable release, while exact tags remain copyable and auditable.
914
93
 
915
- ## How the loop works
94
+ ## What it does—and does not do
916
95
 
917
- 1. Read the project inventory and exact installed npm graph.
918
- 2. Query OSV and GitHub Advisory Database with every installed `name@version` pair, then merge matching GHSA/CVE aliases while preserving which source(s) confirmed the result. Native DSH also queries CISA KEV and FIRST EPSS for matched CVEs; CLI and Action users opt in with `--threat-intel` or `threat-intel: true`.
919
- 3. Watch npm releases for the installed plugin and DSH/Cordis packages.
920
- 4. Create or update one durable incident with the exact dependency path.
921
- 5. Persist a constrained analysis task before delivery.
922
- 6. Route a plugin-originated follow-up to the DSH root whose session workspace matches the project.
923
- 7. Record the exact DSH message/session delivery and accept only the matching model response with strict JSON.
924
- 8. Keep the task on disk when no Agent is available; cancel stale work and conclusions when the incident resolves or changes.
96
+ | It does | It does not claim |
97
+ | --- | --- |
98
+ | Reconstruct exact npm/pnpm dependency paths | An empty finding list is a safety certificate |
99
+ | Query OSV and GitHub Advisory evidence for exact versions | A missing provenance statement proves maliciousness |
100
+ | Compare source and published artifact evidence | Static review replaces sandboxing or runtime testing |
101
+ | Check DSH bundle/profile compatibility without business execution | “Compatible” means the plugin is secure |
102
+ | Monitor old new upstream observations | An LLM can repair evidence that was never collected |
925
103
 
926
- For a local process or a scheduled runner, the same loop is available as:
104
+ ## Install and connect to DSH
927
105
 
928
106
  ```bash
929
- pnpm dlx --package=upstream-radar@latest upstream-radar radar watch ./upstream-radar.config.json --interval 1800
930
- ```
931
-
932
- Use `radar check --frozen --state :memory: --fail-on high --fail-on-compatibility breaking --json` for a machine-enforced CI check against a reviewed graph. The local DSH path continues to use `radar watch`, which refreshes the selected profile before each cycle.
933
-
934
- The handoff uses `ctx.agents.roots()[0].followup(...)` with:
935
-
936
- ```json
937
- {
938
- "kind": "plugin",
939
- "plugin": "upstream-radar",
940
- "form": "notice"
941
- }
942
- ```
943
-
944
- It is a native DSH lifecycle integration—not a chat bridge or a remote-control bot.
945
-
946
- ## Why package-name alerts are not enough
947
-
948
- Given this installed graph:
949
-
950
- ```text
951
- plugin@1.0.0
952
- ├── framework@2.4.7
953
- │ ├── parser@3.2.1
954
- │ └── archive@1.8.0
955
- └── logger@4.0.2
956
- └── parser@2.9.0
957
- ```
107
+ pnpm add upstream-radar
958
108
 
959
- an advisory affecting `parser@2.9.0` matches only the `plugin -> logger -> parser` branch. The unaffected `parser@3.2.1` remains a distinct physical node instead of becoming a package-name false positive.
960
-
961
- ## Vulnerabilities are only half the upstream problem
962
-
963
- Upstream Radar also watches candidate releases for compatibility boundaries that matter to DSH plugins:
964
-
965
- - Node.js engine exclusions;
966
- - incompatible `@deepseek-ai/dsh-*` or `@deepseek-ai/cordis` peer ranges;
967
- - changed `main`, `exports`, or DSH bundle patch paths;
968
- - removed dependencies;
969
- - major and pre-1.0 breaking version boundaries;
970
- - publisher-declared breaking changes in supplied release notes, including public GitHub Release notes attached to the candidate version when npm points to a GitHub repository.
971
-
972
- These are signals for project analysis, not automatic claims that an upgrade is broken.
973
-
974
- ## The model gets judgment, not control of the facts
975
-
976
- Upstream Radar determines facts that a model must not guess:
977
-
978
- ```text
979
- parser@2.9.0 is reported as affected
980
- plugin -> logger -> parser is the installed path
981
- the project runs Node.js 22
982
- the candidate requires Node.js >=24
983
- the installed DSH peer is outside the candidate range
984
- ```
985
-
986
- The DSH Agent answers the repository-specific questions:
987
-
988
- ```text
989
- is the vulnerable feature reachable here?
990
- can attacker-controlled input reach it?
991
- which API or Cordis configuration would the upgrade disturb?
992
- what is the least disruptive project-specific action?
109
+ # Generate a reviewable DSH profile inventory from the installed profile
110
+ npx --yes upstream-radar@0.34.0 setup
993
111
  ```
994
112
 
995
- When one DSH runtime release changes `@deepseek-ai/dsh`, several `@deepseek-ai/dsh-*` packages, or Cordis packages, Radar keeps each package as an independent state record but combines the same project's notices into one Agent analysis. You get one coherent upgrade question without losing the exact package evidence needed for later resolution. The resulting conclusion is copied back to each still-current incident only after the grouped model response passes the same strict validation.
996
-
997
- Advisories, release notes, links, package names, and repository strings remain untrusted data. The generated task requires read-only analysis, project evidence, explicit uncertainty, and a fixed [result schema](schemas/analysis-result.schema.json).
998
-
999
- ## What works today
113
+ For Feishu/webhook routing, DSH Agent handoff, observer state, report schemas, and troubleshooting, use the [full Chinese guide](docs/README.zh-CN.md). The [architecture notes](docs/architecture.md) explain the boundaries and evidence model.
1000
114
 
1001
- - installed DSH `node_modules` graphs and npm lockfile graphs with duplicate versions and bounded dependency paths;
1002
- - DSH shared host-runtime dependency resolution discovered from the running DSH process, with profile and `dsh-host` packages kept distinct in both graphs and alerts;
1003
- - exact `@deepseek-ai/dsh` executable-package evidence, including host-boundary OSV alerts and its own npm compatibility stream;
1004
- - one project-level alert for a shared DSH host-runtime vulnerability, retaining every affected plugin root and exact path instead of sending duplicate per-plugin notices;
1005
- - exact-version OSV vulnerability and malicious-package matching;
1006
- - independent GitHub Advisory Database matching for exact npm versions, with GHSA/CVE alias deduplication, merged fix versions, and source-specific health;
1007
- - npm release monitoring for plugins and DSH/Cordis packages, accepting only a candidate newer than the installed exact version (a regressed `latest` dist-tag is not a breaking update), with public GitHub Release notes attached when an exact candidate tag is available;
1008
- - bounded transitive dependency graph checks for the earliest candidate versions, exact OSV matching for every resolved node, vulnerable path evidence, and explicit incomplete/unavailable coverage;
1009
- - durable incident state with current-task replacement and resolution;
1010
- - a bounded transition history with a local `radar history` audit command;
1011
- - strict DSH result writeback bound to the exact message, session, task, and event, with stale-result rejection;
1012
- - native DSH bundle installation, startup polling, `agent/created` retry, and plugin-source attribution;
1013
- - optional provider-neutral HTTPS webhook delivery for changed events, with endpoint-safe deduplication and retry, project-specific environment routes with independent outboxes, plus direct Feishu/Lark V2 text delivery;
1014
- - delivery-only notification controls for per-project minimum vulnerability severity and timezone-aware quiet hours; critical and malicious-package alerts bypass them, while held DSH tasks and webhook events remain durable;
1015
- - read-only pnpm v6/v9 lockfile graph extraction, including project-root importers and explicit ambiguous peer references;
1016
- - static Radar inventory generation from npm or pnpm lockfiles, followed by the same exact-version OSV check used by the DSH monitor;
1017
- - automatic selection of the only DSH profile with third-party bundles, plus a network-free `radar status` snapshot;
1018
- - commit-friendly `init` output that records the project workspace as `.` by default;
1019
- - a reusable GitHub Action that turns the reviewed graph into a two-step, frozen CI gate;
1020
- - a concise escaped GitHub Job Summary alongside the Action's raw JSON result;
1021
- - an opt-in GitHub Action DSH load matrix for exact published plugin versions and exact DSH versions;
1022
- - a real DSH plugin consumer smoke that runs the published Action against 18 exact package versions;
1023
- - an actionable, network-free `radar status` summary with exact active paths, candidate signals, and next steps;
1024
- - a network-free `doctor` command that checks local DSH registration, overlay/config alignment, state readability, and dependency coverage;
1025
- - compatibility signals for Node.js, peers, exports, entrypoints, bundle paths, dependencies, and version boundaries;
1026
- - top-level remediation evidence that compares active vulnerability ids and aliases with complete candidate graphs, identifying the first non-blocked plugin candidate that removes all checked paths without calling it safe;
1027
- - an opt-in CI gate for confirmed/strong (`breaking`) or all (`any`) active compatibility changes;
1028
- - an offline `benchmark compatibility` command that locks the deterministic rule and gate behavior into six reviewable contracts;
1029
- - disposable `probe dsh-load` and `probe dsh-matrix` commands that check one exact DSH version or a bounded exact-version matrix against one bundle and return `compatible`, `incompatible`, or `unknown`;
1030
- - network-free Radar and real DSH runtime showcases.
1031
-
1032
- The bounded pre-install scanner remains available as a supporting collector. It
1033
- accepts either a local directory or a public GitHub repository URL:
115
+ ## Development
1034
116
 
1035
117
  ```bash
1036
- pnpm dlx --package=upstream-radar@latest upstream-radar scan /path/to/dsh-plugin
1037
- pnpm dlx --package=upstream-radar@latest upstream-radar scan https://github.com/owner/repository --fail-on never
1038
- pnpm dlx --package=upstream-radar@latest upstream-radar inspect dsh-cloudflare-browser-run@0.1.1 --deep
118
+ pnpm install
119
+ pnpm test
120
+ pnpm run release:check
1039
121
  ```
1040
122
 
1041
- The default text gate exits `2` for `review` or `block`, which is useful when every uncertainty needs a human decision. If CI should fail only on hard blocks while still printing review evidence, add `--fail-on block`.
1042
-
1043
- ## Current boundaries
1044
-
1045
- - `init` discovers the only DSH profile with third-party bundles when `--profile` is omitted; multiple candidates still require an explicit profile. By default it follows the installed DSH `node_modules` tree, so pnpm overrides and local resolution choices are included. `--dsh-patch <path>` writes an explicit DSH overlay so first startup needs no environment variables and preserves an explicitly selected registry. `graph npm-lock` and `graph pnpm-lock` are separate pre-install/CI collectors; they do not themselves query OSV or create a Radar config.
1046
- - `init --pnpm-lock <path>` or `init --npm-lock <path>` creates a static config without a DSH profile; it reads `package.json` beside the lockfile unless `--root <name>@<version>` is supplied. Follow it with `radar check` or `radar watch` to query OSV. It does not itself start DSH or deliver Agent tasks.
1047
- - If the plugin root is not published to the selected npm registry, a registry `404` skips only that package's release comparison; exact lockfile dependencies and published DSH host packages are still checked. Registry outages, timeouts, malformed responses, and OSV failures remain operational errors.
1048
- - A graph with unresolved required dependency declarations is marked as incomplete coverage; optional packages that are not installed for the current platform remain visible but do not create a false required-dependency alert. Missing `@deepseek-ai/dsh`, `@deepseek-ai/dsh-*`, and Cordis peers are called out separately as unobserved DSH host dependencies because Radar cannot query a version it was never shown. If the DSH executable package itself cannot be read from the active host plane, the core-runtime boundary remains unknown rather than being guessed.
1049
- - Candidate upgrade graphs are resolved only for a bounded earliest prefix. A candidate with an incomplete or unavailable graph is not recommended; later unqueried candidates remain visibly unchecked. Pass `--no-deep-candidates` to opt out of this extra registry work.
1050
- - When an active vulnerability belongs to an installed plugin, candidate remediation is narrower than an upgrade recommendation: `removed` means the complete checked candidate graph has no matching OSV finding, `still-affected` means a matching path remains, and `unknown` means the graph/source is incomplete, truncated, unavailable, or the path comes from the shared DSH host runtime. A remediation candidate still needs DSH project analysis.
1051
- - Compatibility CI gating is opt-in: `--fail-on-compatibility breaking` fails on confirmed or strong incompatibility signals, while `any` fails on every active compatibility event; neither setting claims that a candidate is safe.
1052
- - `probe dsh-load` is intentionally narrower than a security scan: a successful load proves only that the selected DSH profile accepted the bundle configuration. It does not execute plugin actions, compare capabilities, or grant admission to an unreviewed package.
1053
- - `probe dsh-matrix` is intentionally sequential and bounded to eight versions. An incomplete matrix is not green: `unknown` propagates to the aggregate result until every selected DSH version has a reliable load result.
1054
- - `radar check/watch --frozen` intentionally uses the graph committed in the config for CI; it does not prove that the installed DSH profile has not changed. Without `--frozen`, native DSH and CLI polling refresh the selected profile first.
1055
- - `radar status` is a local snapshot only: it does not refresh OSV, GitHub Advisory, npm, or GitHub Release data, and it cannot prove that a source is current until a check has completed. It does show whether a captured DSH host plane came from the running process or a profile fallback. Its next steps are guidance, not an automatic upgrade or safety decision.
1056
- - `doctor` checks local wiring only; it cannot prove that a running DSH process has delivered a task to a model or that upstream feeds are current.
1057
- - npm and pnpm lock graphs are supported; Yarn graph extraction is not implemented.
1058
- - OSV, GitHub Advisory Database, npm `latest`, and public GitHub Release notes are live sources; changelog, comparison-diff, and migration-guide ingestion are deferred.
1059
- - A failed advisory-source check preserves confirmed matches and returns a visible source warning; each source's health is durable and routed through DSH after three consecutive failures. When OSV and GitHub Advisory Database disagree about severity or fixed versions, the event keeps both source-labeled claims and makes the conflict explicit instead of silently choosing one. Changed events can be sent to a provider-neutral HTTPS endpoint or directly to a Feishu/Lark V2 bot; there is no separate hosted alerting service.
1060
- - `radar watch` is a CLI monitoring fallback; it does not deliver tasks into DSH by itself.
1061
- - Delivery uses one root Agent as the simple default; when several roots exist, it requires an exact project-workspace match and leaves ambiguous tasks queued instead of guessing.
1062
- - DSH result writeback accepts only the exact six-field JSON contract from the matching model session; it does not infer conclusions from ordinary chat or tool output. The result is advisory and never changes deterministic incident state.
1063
- - No Issue, branch, Pull Request, dependency override, or merge is created automatically.
1064
-
1065
- Upstream Radar is alpha software built for the developer-preview DSH ecosystem. Event schemas and adapter boundaries can change.
1066
-
1067
- ## Project guide
1068
-
1069
- - [Architecture](docs/architecture.md)
1070
- - [DSH headless showcase](examples/dsh/README.md)
1071
- - [Radar showcase walkthrough](docs/showcase.md)
1072
- - [Product vision(中文)](docs/vision.zh-CN.md)
1073
- - [Checks and evidence(中文)](docs/checks.zh-CN.md)
1074
- - [Threat model](docs/threat-model.md)
1075
- - [Roadmap](ROADMAP.md)
1076
- - [Changelog](CHANGELOG.md)
1077
- - [Release process](docs/releasing.md)
1078
- - [Contributing](CONTRIBUTING.md)
1079
- - [Security policy](SECURITY.md)
1080
-
1081
- If DSH plugins are part of your stack, star the repository to follow the upstream safety loop as it grows. Start with the [reproducible DSH handoff showcase](https://github.com/MicroMilo/upstream-radar/discussions/11), then share questions and design feedback in [GitHub Discussions](https://github.com/MicroMilo/upstream-radar/discussions).
1082
-
1083
- <sub>Community project for DeepSeek Harness. Not an official DeepSeek product. Apache-2.0 licensed.</sub>
123
+ The project is Apache-2.0 licensed. Contributions that improve a real DSH plugin report, dependency resolution, advisory matching, or reproducible author feedback are especially welcome.