upstream-radar 0.33.0 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +233 -13
- package/dist/src/cli.js +173 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/dsh-profile-check.d.ts +79 -0
- package/dist/src/dsh-profile-check.d.ts.map +1 -0
- package/dist/src/dsh-profile-check.js +600 -0
- package/dist/src/dsh-profile-check.js.map +1 -0
- package/dist/src/github-advisory.d.ts.map +1 -1
- package/dist/src/github-advisory.js +9 -4
- package/dist/src/github-advisory.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/npm.d.ts.map +1 -1
- package/dist/src/npm.js +9 -1
- package/dist/src/npm.js.map +1 -1
- package/dist/src/render.d.ts.map +1 -1
- package/dist/src/render.js +17 -2
- package/dist/src/render.js.map +1 -1
- package/dist/src/scan.d.ts.map +1 -1
- package/dist/src/scan.js +39 -0
- package/dist/src/scan.js.map +1 -1
- package/dist/src/upstream-observer.d.ts +224 -0
- package/dist/src/upstream-observer.d.ts.map +1 -0
- package/dist/src/upstream-observer.js +1342 -0
- package/dist/src/upstream-observer.js.map +1 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/docs/README.zh-CN.md +213 -14
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
<a href="#see-one-incident">See one incident</a> ·
|
|
30
30
|
<a href="#install-in-dsh">Install in DSH</a> ·
|
|
31
31
|
<a href="#notify-feishu-or-an-https-endpoint">Notify Feishu</a> ·
|
|
32
|
+
<a href="#observe-dsh-plugin-upstream-changes">Observe upstream changes</a> ·
|
|
32
33
|
<a href="#run-the-proof">Run the proof</a> ·
|
|
33
34
|
<a href="#run-it-in-github-actions">Run in GitHub Actions</a> ·
|
|
34
35
|
<a href="https://github.com/MicroMilo/upstream-radar/issues/new?template=trial.yml">Share feedback</a> ·
|
|
@@ -51,7 +52,9 @@ It looks only at the current directory and local DSH profile metadata. It recomm
|
|
|
51
52
|
| Your goal | Start here | What you get |
|
|
52
53
|
| --- | --- | --- |
|
|
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. |
|
|
54
56
|
| 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. |
|
|
57
|
+
| 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. |
|
|
55
58
|
| 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. |
|
|
56
59
|
| 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. |
|
|
57
60
|
| Review one exact published artifact | `upstream-radar inspect npm:<package>@<exact-version> --deep` | Package, dependency, vulnerability, and provenance evidence for one release. |
|
|
@@ -84,6 +87,28 @@ Next: Review the fixed version with the DSH Agent in this project.
|
|
|
84
87
|
|
|
85
88
|
The useful part is the exact path and project-specific next step—not another generic list of vulnerable package names.
|
|
86
89
|
|
|
90
|
+
Want to try it on a real published DSH plugin immediately?
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npx --yes upstream-radar@0.33.1 inspect npm:dsh-feishu-bot@0.15.4 --deep
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This runs from an otherwise empty directory and returns a short admission,
|
|
97
|
+
coverage, dependency-count, vulnerability-count, and next-step summary.
|
|
98
|
+
|
|
99
|
+
Want to see a real author-actionable result? This exact published DSH plugin
|
|
100
|
+
currently cannot produce a complete dependency graph in a clean npm resolver:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npx --yes upstream-radar@0.33.1 inspect \
|
|
104
|
+
npm:@sanqi-normal/dsh-webui-market-plugin@0.5.4 \
|
|
105
|
+
--deep --fail-on never
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The result is `review / incomplete`, with the concrete cause
|
|
109
|
+
`@deepseek-ai/dsh-compact@^0.0.1-rc.1` not published. No DSH profile, plugin
|
|
110
|
+
execution, or LLM is required. See the [reproducible author report](examples/dsh/reports/sanqi-market-plugin-dependency-resolution.md).
|
|
111
|
+
|
|
87
112
|
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.
|
|
88
113
|
|
|
89
114
|
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.
|
|
@@ -144,6 +169,181 @@ pnpm dlx --package=upstream-radar@latest upstream-radar radar watch ./upstream-r
|
|
|
144
169
|
|
|
145
170
|
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.
|
|
146
171
|
|
|
172
|
+
## Check a DSH profile before starting it
|
|
173
|
+
|
|
174
|
+
When the concern is “will this profile boot with the packages and patch rows it
|
|
175
|
+
actually has?”, use the static profile check first:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pnpm run build
|
|
179
|
+
node dist/src/cli.js profile-check "$DSH_HOME/profiles/web" \
|
|
180
|
+
--report ./dsh-profile-check.md
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
For the shortest answer, add `--summary`:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
pnpm dlx --package=upstream-radar@latest upstream-radar profile-check \
|
|
187
|
+
"$DSH_HOME/profiles/web" --summary
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
When `DSH_HOME` contains exactly one profile with third-party bundles, the
|
|
191
|
+
directory can be omitted:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npx --yes upstream-radar@latest profile-check --summary
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
With no eligible profile, or more than one, Radar prints the names it found and
|
|
198
|
+
asks for an explicit directory; it never guesses between multiple profiles.
|
|
199
|
+
|
|
200
|
+
It prints only the status, the important evidence, the reason, and the next
|
|
201
|
+
repair. The exit code remains `2` for a blocked profile and `0` for a pass.
|
|
202
|
+
|
|
203
|
+
It reads the profile manifest, pnpm/npm lockfile, package metadata,
|
|
204
|
+
`pnpm-workspace.yaml`, and `cordis.patch.yml`. It catches the two concrete
|
|
205
|
+
failure shapes from [dsh-web-ui #71](https://github.com/zhu1090093659/dsh-web-ui/issues/71)
|
|
206
|
+
and [#35](https://github.com/zhu1090093659/dsh-web-ui/issues/35): a loader row
|
|
207
|
+
that names a package absent from the locked profile, and the same loader id
|
|
208
|
+
being inserted twice. It also points out a pnpm `minimumReleaseAge` policy that
|
|
209
|
+
does not exempt the plugin, because that can keep a newly fixed plugin on an
|
|
210
|
+
older release during its cooling window.
|
|
211
|
+
|
|
212
|
+
This is deliberately a pre-start check: no network, installation, plugin code,
|
|
213
|
+
DSH process, Agent, or LLM is involved. A blocked result exits with code `2`.
|
|
214
|
+
The complete replay is `pnpm run showcase:dsh-profile-check`; it runs the
|
|
215
|
+
public case before the fix, after the manual package workaround, and after the
|
|
216
|
+
correct bundled-carrier fix.
|
|
217
|
+
|
|
218
|
+
The short, author-facing result is `pnpm run showcase:dsh-case`. It turns the
|
|
219
|
+
same three static checks into one repair story: the old profile is blocked,
|
|
220
|
+
manually adding the missing package creates a duplicate loader, and the
|
|
221
|
+
bundled-carrier update reaches `pass`. If an OpenAI-compatible `issue-locator`
|
|
222
|
+
model is available, pass its env file with
|
|
223
|
+
`ISSUE_LOCATOR_ENV_FILE=/path/to/issue-locator/.env`; the model only explains
|
|
224
|
+
the already-checked facts. If the endpoint is unavailable, the command still
|
|
225
|
+
prints the deterministic evidence explanation and records that the fallback was
|
|
226
|
+
used. Add `:report` to write the [case analysis result](examples/dsh/reports/dsh-web-ui-issue-71-analysis.json).
|
|
227
|
+
|
|
228
|
+
We also ran the current static checks against the first 50 entries in the DSH
|
|
229
|
+
plugin registry. That batch found **0 confirmed runtime dependency
|
|
230
|
+
vulnerabilities**. It did find real monitoring-quality problems—development-only
|
|
231
|
+
dependencies mixed into source lockfiles, three plugin lockfiles whose root
|
|
232
|
+
version lagged the source manifest, and a tarball format the scanner could not
|
|
233
|
+
parse. The [batch report](examples/dsh/reports/dsh-batch-50-2026-08-17.md)
|
|
234
|
+
keeps those results honest; it is not marketed as a vulnerability hit list.
|
|
235
|
+
|
|
236
|
+
## Observe DSH plugin upstream changes
|
|
237
|
+
|
|
238
|
+
This is the upstream-change loop: instead of polling every vulnerability source on
|
|
239
|
+
every run, Radar remembers one observation point per plugin and asks what changed
|
|
240
|
+
since then.
|
|
241
|
+
|
|
242
|
+
```text
|
|
243
|
+
targets.yml
|
|
244
|
+
↓
|
|
245
|
+
GitHub commit + npm package metadata + optional lockfile
|
|
246
|
+
↓
|
|
247
|
+
observations.json
|
|
248
|
+
↓
|
|
249
|
+
old → new comparison
|
|
250
|
+
↓
|
|
251
|
+
only meaningful changes → DSH Agent task → report
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Start from the [copyable target example](examples/upstream-observer/targets.yml):
|
|
255
|
+
|
|
256
|
+
```yaml
|
|
257
|
+
schema: upstream-radar.observer-targets/v1alpha1
|
|
258
|
+
targets:
|
|
259
|
+
- id: my-dsh-plugin
|
|
260
|
+
ecosystem: dsh
|
|
261
|
+
repository: acme/my-dsh-plugin
|
|
262
|
+
ref: main
|
|
263
|
+
package: my-dsh-plugin
|
|
264
|
+
packagePath: plugin/package.json
|
|
265
|
+
lockfile: plugin/pnpm-lock.yaml
|
|
266
|
+
lockfileType: pnpm
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Then run one cycle:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
export GITHUB_TOKEN='a read-only GitHub token'
|
|
273
|
+
pnpm run build
|
|
274
|
+
node dist/src/cli.js observe \
|
|
275
|
+
./targets.yml \
|
|
276
|
+
--state ./observations.json \
|
|
277
|
+
--report ./upstream-radar-observer.md
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
This command uses the checked-out source so it is runnable before the next npm
|
|
281
|
+
release. After a release includes `observe`, pin that exact version in CI rather
|
|
282
|
+
than relying on `latest`.
|
|
283
|
+
|
|
284
|
+
The first cycle only creates a baseline. Later cycles compare:
|
|
285
|
+
|
|
286
|
+
- the source commit and changed files;
|
|
287
|
+
- the published npm version and integrity value;
|
|
288
|
+
- the package entrypoint, exports, Node requirement, DSH bundle metadata and dependency declarations;
|
|
289
|
+
- the real npm or pnpm lockfile graph, when a lockfile is configured.
|
|
290
|
+
|
|
291
|
+
README/docs/tests-only changes advance the observation point without waking the
|
|
292
|
+
Agent. Runtime source, DSH bundle, package entry, dependency graph, npm version,
|
|
293
|
+
or npm integrity changes create an old → new task. If the Agent is not configured,
|
|
294
|
+
the task stays in `observations.json`; no plugin is installed or executed.
|
|
295
|
+
|
|
296
|
+
If you do not have a DSH wrapper configured yet, you can point the observer at
|
|
297
|
+
an existing issue-locator/OpenAI-compatible `.env` file instead:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
upstream-radar observe ./targets.yml \
|
|
301
|
+
--state ./observations.json \
|
|
302
|
+
--llm-env-file /path/to/issue-locator/.env
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Radar reads only the endpoint, API key, and model name for that call. It never
|
|
306
|
+
writes the key or endpoint into the observation state or report. The model is
|
|
307
|
+
called only after a meaningful upstream change; a baseline or docs-only change
|
|
308
|
+
does not call it. If the endpoint is unavailable, the deterministic change
|
|
309
|
+
record remains pending and can be retried with `--retry-pending`.
|
|
310
|
+
|
|
311
|
+
The env file may use the issue-locator names (`ISSUE_LOCATOR_LLM_*`), the common
|
|
312
|
+
OpenAI names (`OPENAI_BASE_URL`, `OPENAI_API_KEY`, `OPENAI_MODEL`), or `MODEL` /
|
|
313
|
+
`CODEX_MODEL` for the model name.
|
|
314
|
+
For ModelBest-style URLs, a 404 on `/llm/v1` also retries `/llm/openai/v1`.
|
|
315
|
+
|
|
316
|
+
The scheduled workflow is [examples/github-actions/upstream-observer.yml](examples/github-actions/upstream-observer.yml).
|
|
317
|
+
The checked-in workflow is a dogfood workflow for this repository: it checks out
|
|
318
|
+
and builds Radar before running the observer. It persists only the observation
|
|
319
|
+
point. A quiet run does not create a daily commit.
|
|
320
|
+
|
|
321
|
+
The workflow also supports three optional repository secrets—
|
|
322
|
+
`ISSUE_LOCATOR_LLM_BASE_URL`, `ISSUE_LOCATOR_LLM_API_KEY`, and
|
|
323
|
+
`ISSUE_LOCATOR_LLM_MODEL`. When all three exist, the job sends meaningful tasks
|
|
324
|
+
to the issue-locator/OpenAI-compatible model. When they do not exist, static
|
|
325
|
+
upstream observation still runs and the job does not pretend that model analysis
|
|
326
|
+
was performed.
|
|
327
|
+
|
|
328
|
+
### The DSH Agent boundary
|
|
329
|
+
|
|
330
|
+
The observer accepts an explicit executable through `--dsh-agent-command`. It
|
|
331
|
+
writes one bounded, read-only task prompt to stdin and expects one JSON conclusion
|
|
332
|
+
on stdout. The command is started without a shell, and the prompt treats every
|
|
333
|
+
remote repository string and release field as untrusted evidence.
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
upstream-radar observe ./targets.yml \
|
|
337
|
+
--state ./observations.json \
|
|
338
|
+
--dsh-agent-command /path/to/reviewed-dsh-agent-wrapper \
|
|
339
|
+
--dsh-agent-arg --json
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Radar does not guess an undocumented `dsh` CLI subcommand. A reviewed DSH
|
|
343
|
+
headless wrapper is the integration boundary; this keeps the observer usable in
|
|
344
|
+
GitHub Actions and lets the DSH adapter evolve without changing observation or
|
|
345
|
+
diff logic. Use `--retry-pending` to deliver tasks left by a previous run.
|
|
346
|
+
|
|
147
347
|
## Notify Feishu or an HTTPS endpoint
|
|
148
348
|
|
|
149
349
|
To also notify a team-owned HTTPS endpoint when an incident changes, keep the endpoint outside the reviewed config and state:
|
|
@@ -322,7 +522,7 @@ cd my-dsh-plugin
|
|
|
322
522
|
pnpm install --ignore-scripts
|
|
323
523
|
|
|
324
524
|
# Read the exact graph before adding the plugin to a DSH profile.
|
|
325
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
525
|
+
pnpm dlx --package=upstream-radar@0.33.1 upstream-radar graph pnpm-lock pnpm-lock.yaml --json
|
|
326
526
|
```
|
|
327
527
|
|
|
328
528
|
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`:
|
|
@@ -344,7 +544,7 @@ jobs:
|
|
|
344
544
|
runs-on: ubuntu-latest
|
|
345
545
|
steps:
|
|
346
546
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
347
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
547
|
+
- uses: MicroMilo/upstream-radar@v0.33.1
|
|
348
548
|
with:
|
|
349
549
|
fail-on: high
|
|
350
550
|
fail-on-compatibility: breaking
|
|
@@ -352,6 +552,22 @@ jobs:
|
|
|
352
552
|
|
|
353
553
|
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.
|
|
354
554
|
|
|
555
|
+
To check a real published DSH artifact directly, run one command:
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
npx --yes upstream-radar@0.33.1 inspect npm:dsh-feishu-bot@0.15.4 --deep
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
The checked result is `REVIEW`: registry integrity, signature, provenance, and
|
|
562
|
+
89 resolved packages are verified; known vulnerabilities are `0`, while 12
|
|
563
|
+
optional dependency edges remain unresolved. That is a useful author result:
|
|
564
|
+
the empty vulnerability list is visible, but it is not mislabeled as a full
|
|
565
|
+
`ALLOW` decision.
|
|
566
|
+
|
|
567
|
+
The same exact tarball loads in both DSH `0.1.0-rc.6` and `0.1.0-rc.7` disposable
|
|
568
|
+
profiles. See the [real compatibility probe](examples/dsh/reports/dsh-feishu-bot-0.15.4-probe.md)
|
|
569
|
+
for the command and boundary.
|
|
570
|
+
|
|
355
571
|
## See one incident
|
|
356
572
|
|
|
357
573
|
If an advisory affects only one of two installed `parser` versions, Radar reports the path that actually matched:
|
|
@@ -483,7 +699,11 @@ To demonstrate the host-runtime dependency path specifically, run `pnpm run show
|
|
|
483
699
|
|
|
484
700
|
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).
|
|
485
701
|
|
|
486
|
-
To validate the actual first-use path against
|
|
702
|
+
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.
|
|
703
|
+
|
|
704
|
+
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.
|
|
705
|
+
|
|
706
|
+
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.
|
|
487
707
|
|
|
488
708
|
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.
|
|
489
709
|
|
|
@@ -492,7 +712,7 @@ To see the two-source vulnerability contract without contacting the network, run
|
|
|
492
712
|
Before wiring a project into a compatibility gate, run the offline rule benchmark:
|
|
493
713
|
|
|
494
714
|
```bash
|
|
495
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
715
|
+
pnpm dlx --package=upstream-radar@0.33.1 upstream-radar benchmark compatibility
|
|
496
716
|
```
|
|
497
717
|
|
|
498
718
|
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.
|
|
@@ -505,7 +725,7 @@ When you have an exact plugin artifact and want to know whether one exact DSH re
|
|
|
505
725
|
# Pack an exact npm release without running its lifecycle scripts.
|
|
506
726
|
npm pack --ignore-scripts dsh-plugin@1.2.3
|
|
507
727
|
|
|
508
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
728
|
+
pnpm dlx --package=upstream-radar@0.33.1 upstream-radar probe dsh-load \
|
|
509
729
|
./dsh-plugin-1.2.3.tgz \
|
|
510
730
|
--dsh-version 0.1.0-rc.6
|
|
511
731
|
```
|
|
@@ -531,7 +751,7 @@ It exercises a loadable bundle, a bundle patch DSH rejects, and a package that r
|
|
|
531
751
|
To compare a plugin against more than one DSH release, use the matrix form:
|
|
532
752
|
|
|
533
753
|
```bash
|
|
534
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
754
|
+
pnpm dlx --package=upstream-radar@0.33.1 upstream-radar probe dsh-matrix \
|
|
535
755
|
./dsh-plugin-1.2.3.tgz \
|
|
536
756
|
--dsh-version 0.1.0-rc.3 \
|
|
537
757
|
--dsh-version 0.1.0-rc.6 \
|
|
@@ -547,7 +767,7 @@ If your team wants the shortest scheduled CI gate before wiring a machine to a l
|
|
|
547
767
|
```yaml
|
|
548
768
|
steps:
|
|
549
769
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
550
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
770
|
+
- uses: MicroMilo/upstream-radar@v0.33.1
|
|
551
771
|
with:
|
|
552
772
|
fail-on: high
|
|
553
773
|
# Optional: also fail on deterministic DSH/plugin compatibility breaks.
|
|
@@ -556,12 +776,12 @@ steps:
|
|
|
556
776
|
threat-intel: true
|
|
557
777
|
```
|
|
558
778
|
|
|
559
|
-
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.
|
|
779
|
+
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.1`, and pin the checkout Action in your workflow according to your repository's policy.
|
|
560
780
|
|
|
561
781
|
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:
|
|
562
782
|
|
|
563
783
|
```yaml
|
|
564
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
784
|
+
- uses: MicroMilo/upstream-radar@v0.33.1
|
|
565
785
|
with:
|
|
566
786
|
fail-on: high
|
|
567
787
|
```
|
|
@@ -571,7 +791,7 @@ An existing `config` wins over auto-detection. If both lockfiles exist, or neith
|
|
|
571
791
|
To review the exact plugin artifact before it enters DSH, add `inspect-package`:
|
|
572
792
|
|
|
573
793
|
```yaml
|
|
574
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
794
|
+
- uses: MicroMilo/upstream-radar@v0.33.1
|
|
575
795
|
with:
|
|
576
796
|
inspect-package: dsh-cloudflare-browser-run@0.1.1
|
|
577
797
|
# review is the safe default; use block only when incomplete coverage may pass.
|
|
@@ -583,7 +803,7 @@ This downloads that exact npm tarball, verifies the registry integrity/signature
|
|
|
583
803
|
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):
|
|
584
804
|
|
|
585
805
|
```yaml
|
|
586
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
806
|
+
- uses: MicroMilo/upstream-radar@v0.33.1
|
|
587
807
|
with:
|
|
588
808
|
pnpm-lock: pnpm-lock.yaml
|
|
589
809
|
fail-on: high
|
|
@@ -597,7 +817,7 @@ See the [copyable npm workflow](examples/github-actions/upstream-radar-npm.yml)
|
|
|
597
817
|
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:
|
|
598
818
|
|
|
599
819
|
```bash
|
|
600
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
820
|
+
pnpm dlx --package=upstream-radar@0.33.1 upstream-radar radar check \
|
|
601
821
|
./upstream-radar.config.json --frozen --state :memory: --fail-on high \
|
|
602
822
|
--fail-on-compatibility breaking --json
|
|
603
823
|
```
|
|
@@ -605,7 +825,7 @@ pnpm dlx --package=upstream-radar@0.33.0 upstream-radar radar check \
|
|
|
605
825
|
To add the optional DSH load matrix for a published plugin, provide an exact npm package and at least two exact DSH versions:
|
|
606
826
|
|
|
607
827
|
```yaml
|
|
608
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
828
|
+
- uses: MicroMilo/upstream-radar@v0.33.1
|
|
609
829
|
id: radar
|
|
610
830
|
with:
|
|
611
831
|
config: upstream-radar.config.json
|
package/dist/src/cli.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import process from 'node:process';
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import { access, readFile } from 'node:fs/promises';
|
|
4
|
+
import { access, readFile, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { dirname, join, resolve } from 'node:path';
|
|
6
6
|
import { renderCompatibilityBenchmark, runCompatibilityBenchmark } from './compatibility-benchmark.js';
|
|
7
7
|
import { assessCompatibilityChange } from './compatibility.js';
|
|
8
8
|
import { probeDshLoad, probeDshLoadMatrix, renderDshLoadMatrix, renderDshLoadProbe } from './dsh-probe.js';
|
|
9
9
|
import { createAnalysisTask, renderAgentAnalysisPrompt } from './dsh-analysis.js';
|
|
10
10
|
import { createDoctorReport, renderDoctorReport } from './doctor.js';
|
|
11
|
+
import { checkDshProfile, renderDshProfileCheck, renderDshProfileCheckSummary } from './dsh-profile-check.js';
|
|
11
12
|
import { createDemoReport, renderDemo } from './demo.js';
|
|
12
13
|
import { GitHubReleaseClient } from './github-release.js';
|
|
13
14
|
import { parseNpmLockGraph, parsePnpmLockGraph } from './graph.js';
|
|
@@ -30,6 +31,7 @@ import { createRadarNext, createRadarStatus, renderRadarNext, renderRadarStatus
|
|
|
30
31
|
import { renderTextReport } from './render.js';
|
|
31
32
|
import { scanDirectory } from './scan.js';
|
|
32
33
|
import { CisaKevClient, EpssClient } from './threat-intel.js';
|
|
34
|
+
import { loadObservationState, parseObserverConfigText, renderObserverReport, runDshAgentCommand, runOpenAiCompatibleAgent, runObserver, saveObservationState, UpstreamObserverClient, } from './upstream-observer.js';
|
|
33
35
|
import { TOOL_VERSION } from './version.js';
|
|
34
36
|
import { eventsForRadarWebhookTarget, markRadarWebhookEventsDelivered, markRadarWebhookEventsDeliveredForRoute, normalizeRadarWebhookUrl, queueRadarWebhookEvents, queueRadarWebhookEventsForRoute, resolveRadarWebhookTargets, sendRadarWebhook, undeliveredRadarWebhookEvents, undeliveredRadarWebhookEventsForRoute, } from './webhook.js';
|
|
35
37
|
const VALID_THRESHOLDS = new Set(['warn', 'review', 'block', 'never']);
|
|
@@ -186,6 +188,26 @@ and queries the implemented vulnerability checks. An empty finding list is not
|
|
|
186
188
|
a safety certificate; check the coverage verdict before admitting the package.
|
|
187
189
|
The default gate exits 2 for review or block; use --fail-on block when review
|
|
188
190
|
should remain visible without failing CI.
|
|
191
|
+
`,
|
|
192
|
+
observe: `Upstream Radar — watch DSH plugin repositories and packages for meaningful upstream changes
|
|
193
|
+
|
|
194
|
+
Usage:
|
|
195
|
+
upstream-radar observe <targets.yml> [--state <observations.json>]
|
|
196
|
+
[--report <report.md>] [--dsh-agent-command <executable>]
|
|
197
|
+
[--dsh-agent-arg <argument>] [--llm-env-file <path>] [--retry-pending] [--json]
|
|
198
|
+
|
|
199
|
+
The first run creates a baseline. Later runs compare source commits, published
|
|
200
|
+
npm metadata, package manifests, and an optional npm/pnpm dependency graph. A
|
|
201
|
+
DSH Agent is called only for meaningful changes. Safety: does not install packages, run lifecycle scripts, load plugin code, or invoke a shell.
|
|
202
|
+
|
|
203
|
+
The Agent executable receives one read-only task prompt on stdin and should
|
|
204
|
+
return one JSON conclusion on stdout. If it is not configured, the task stays
|
|
205
|
+
in observations.json for a later explicit retry. As a simpler alternative,
|
|
206
|
+
--llm-env-file reads an OpenAI-compatible issue-locator/.env-style file for
|
|
207
|
+
only the model call. It accepts ISSUE_LOCATOR_LLM_*, OPENAI_*, or MODEL/CODEX_MODEL
|
|
208
|
+
keys; it never writes the key or endpoint to observations.json.
|
|
209
|
+
If a ModelBest-style base URL ends in /llm/v1, a 404 also retries the known
|
|
210
|
+
/llm/openai/v1 path.
|
|
189
211
|
`,
|
|
190
212
|
graph: `Upstream Radar — read a lockfile into the canonical dependency graph
|
|
191
213
|
|
|
@@ -195,6 +217,18 @@ Usage:
|
|
|
195
217
|
|
|
196
218
|
This command is offline and does not install packages, run lifecycle scripts,
|
|
197
219
|
load plugin code, or query vulnerability sources.
|
|
220
|
+
`,
|
|
221
|
+
'profile-check': `Upstream Radar — check one DSH profile before starting it
|
|
222
|
+
|
|
223
|
+
Usage:
|
|
224
|
+
upstream-radar profile-check [profile-directory] [--patch <path>] [--report <path>] [--summary] [--json]
|
|
225
|
+
|
|
226
|
+
Reads the profile package manifest, lockfile, node_modules package metadata,
|
|
227
|
+
pnpm release-age policy, and cordis.patch.yml. It reports loader rows that
|
|
228
|
+
refer to missing packages and duplicate loader ids. It never installs packages,
|
|
229
|
+
starts DSH, loads plugin code, contacts a vulnerability source, or invokes a
|
|
230
|
+
DSH Agent/model. When the directory is omitted, the only DSH profile with
|
|
231
|
+
third-party bundles is selected automatically.
|
|
198
232
|
`,
|
|
199
233
|
probe: `Upstream Radar — test whether a DSH bundle loads in disposable profiles
|
|
200
234
|
|
|
@@ -371,7 +405,9 @@ Usage:
|
|
|
371
405
|
upstream-radar doctor [config.json] [options]
|
|
372
406
|
upstream-radar scan <directory> [--json] [--fail-on <warn|review|block|never>]
|
|
373
407
|
upstream-radar inspect npm:<package>@<exact-version> [--deep] [--json] [--fail-on <warn|review|block|never>]
|
|
408
|
+
upstream-radar observe <targets.yml> [--state <observations.json>] [--report <report.md>] [--dsh-agent-command <executable>] [--dsh-agent-arg <argument>] [--llm-env-file <path>] [--retry-pending] [--json]
|
|
374
409
|
upstream-radar graph <npm-lock|pnpm-lock> <lockfile> [--root <package>@<exact-version>] [--json]
|
|
410
|
+
upstream-radar profile-check [profile-directory] [--patch <path>] [--report <path>] [--summary] [--json]
|
|
375
411
|
upstream-radar probe dsh-load <package.tgz> [--dsh-version <exact-version>] [--timeout <seconds>] [--keep-profile] [--json]
|
|
376
412
|
upstream-radar probe dsh-matrix <package.tgz> --dsh-version <v1>[,<v2>,...] [--timeout <seconds>] [--keep-profile] [--json]
|
|
377
413
|
upstream-radar demo [--json]
|
|
@@ -399,7 +435,9 @@ Commands:
|
|
|
399
435
|
doctor check local Radar/DSH wiring without polling upstream sources
|
|
400
436
|
scan bounded, read-only inspection of a local package directory
|
|
401
437
|
inspect fetch and verify the exact npm artifact before inspecting its contents
|
|
438
|
+
observe compare upstream DSH plugin repositories and route only meaningful changes to a DSH Agent
|
|
402
439
|
graph read a lockfile into the canonical dependency graph without installing packages
|
|
440
|
+
profile-check check a DSH profile's lockfile and patch rows without starting DSH
|
|
403
441
|
probe run a bounded DSH bundle-load check or version matrix in disposable profiles
|
|
404
442
|
demo show the exact-path-to-DSH handoff without network, DSH, or plugin installation
|
|
405
443
|
quickstart choose the smallest first-use path without changing the environment
|
|
@@ -533,6 +571,57 @@ async function runGraph(args) {
|
|
|
533
571
|
].join('\n') + '\n');
|
|
534
572
|
return 0;
|
|
535
573
|
}
|
|
574
|
+
async function runDshProfileCheck(args) {
|
|
575
|
+
let profileDirectory;
|
|
576
|
+
let firstOption = 0;
|
|
577
|
+
if (args[0] !== undefined && !args[0].startsWith('-')) {
|
|
578
|
+
profileDirectory = args[0];
|
|
579
|
+
firstOption = 1;
|
|
580
|
+
}
|
|
581
|
+
let json = false;
|
|
582
|
+
let summary = false;
|
|
583
|
+
let reportPath;
|
|
584
|
+
let patchPath;
|
|
585
|
+
for (let index = firstOption; index < args.length; index += 1) {
|
|
586
|
+
const argument = args[index];
|
|
587
|
+
if (argument === '--json') {
|
|
588
|
+
json = true;
|
|
589
|
+
}
|
|
590
|
+
else if (argument === '--summary') {
|
|
591
|
+
summary = true;
|
|
592
|
+
}
|
|
593
|
+
else if (argument === '--patch' || argument === '--report') {
|
|
594
|
+
const value = args[index + 1];
|
|
595
|
+
if (value === undefined || value.startsWith('-'))
|
|
596
|
+
throw new Error(`${argument} requires a value`);
|
|
597
|
+
if (argument === '--patch')
|
|
598
|
+
patchPath = value;
|
|
599
|
+
else
|
|
600
|
+
reportPath = value;
|
|
601
|
+
index += 1;
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
throw new Error(`unknown option for profile-check: ${argument}`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (profileDirectory === undefined) {
|
|
608
|
+
const profiles = await discoverDshProfiles();
|
|
609
|
+
if (profiles.length === 0) {
|
|
610
|
+
throw new Error('profile-check could not find a DSH profile with third-party bundles; pass <profile-directory> explicitly');
|
|
611
|
+
}
|
|
612
|
+
if (profiles.length > 1) {
|
|
613
|
+
throw new Error(`profile-check found multiple DSH profiles with third-party bundles (${profiles.join(', ')}); pass <profile-directory> explicitly`);
|
|
614
|
+
}
|
|
615
|
+
profileDirectory = resolveDshProfileDirectory(profiles[0]);
|
|
616
|
+
}
|
|
617
|
+
const report = await checkDshProfile({ profileDirectory, ...(patchPath === undefined ? {} : { patchFile: patchPath }) });
|
|
618
|
+
const jsonText = `${JSON.stringify(report, null, 2)}\n`;
|
|
619
|
+
if (reportPath !== undefined) {
|
|
620
|
+
await writeFile(resolve(reportPath), reportPath.endsWith('.json') ? jsonText : summary ? renderDshProfileCheckSummary(report) : renderDshProfileCheck(report));
|
|
621
|
+
}
|
|
622
|
+
process.stdout.write(json ? jsonText : summary ? renderDshProfileCheckSummary(report) : renderDshProfileCheck(report));
|
|
623
|
+
return report.status === 'blocked' ? 2 : 0;
|
|
624
|
+
}
|
|
536
625
|
async function runTask(args) {
|
|
537
626
|
const subcommand = args[0];
|
|
538
627
|
if (subcommand !== 'list' && subcommand !== 'show' && subcommand !== 'ack') {
|
|
@@ -1303,6 +1392,85 @@ async function runRadar(args) {
|
|
|
1303
1392
|
: `${renderRadarEvents(events)}Prepared ${analysisTasks.length} DSH compatibility analysis task(s).\n`);
|
|
1304
1393
|
return 0;
|
|
1305
1394
|
}
|
|
1395
|
+
async function runObserve(args) {
|
|
1396
|
+
const targetsPath = args[0];
|
|
1397
|
+
if (targetsPath === undefined || targetsPath.startsWith('-'))
|
|
1398
|
+
throw new Error('observe requires a targets.yml file');
|
|
1399
|
+
let statePath = 'observations.json';
|
|
1400
|
+
let reportPath;
|
|
1401
|
+
let agentCommand;
|
|
1402
|
+
let agentArgs = [];
|
|
1403
|
+
let llmEnvFile;
|
|
1404
|
+
let registry;
|
|
1405
|
+
let retryPending = false;
|
|
1406
|
+
let json = false;
|
|
1407
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1408
|
+
const argument = args[index];
|
|
1409
|
+
if (argument === '--json') {
|
|
1410
|
+
json = true;
|
|
1411
|
+
}
|
|
1412
|
+
else if (argument === '--retry-pending') {
|
|
1413
|
+
retryPending = true;
|
|
1414
|
+
}
|
|
1415
|
+
else if (argument === '--state' || argument === '--report' || argument === '--dsh-agent-command' || argument === '--dsh-agent-arg' || argument === '--llm-env-file' || argument === '--registry') {
|
|
1416
|
+
const value = args[index + 1];
|
|
1417
|
+
if (value === undefined || (value.startsWith('-') && argument !== '--dsh-agent-arg'))
|
|
1418
|
+
throw new Error(`${argument} requires a value`);
|
|
1419
|
+
if (argument === '--state')
|
|
1420
|
+
statePath = value;
|
|
1421
|
+
else if (argument === '--report')
|
|
1422
|
+
reportPath = value;
|
|
1423
|
+
else if (argument === '--dsh-agent-command')
|
|
1424
|
+
agentCommand = value;
|
|
1425
|
+
else if (argument === '--dsh-agent-arg')
|
|
1426
|
+
agentArgs.push(value);
|
|
1427
|
+
else if (argument === '--llm-env-file')
|
|
1428
|
+
llmEnvFile = value;
|
|
1429
|
+
else
|
|
1430
|
+
registry = value;
|
|
1431
|
+
index += 1;
|
|
1432
|
+
}
|
|
1433
|
+
else {
|
|
1434
|
+
throw new Error(`unknown option for observe: ${argument}`);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
const targetText = await readBoundedFile(targetsPath, 256 * 1024);
|
|
1438
|
+
const config = parseObserverConfigText(targetText);
|
|
1439
|
+
const previousState = await loadObservationState(statePath);
|
|
1440
|
+
const source = new UpstreamObserverClient({
|
|
1441
|
+
...(process.env.GITHUB_TOKEN === undefined ? {} : { githubToken: process.env.GITHUB_TOKEN }),
|
|
1442
|
+
...(registry === undefined ? {} : { registry }),
|
|
1443
|
+
});
|
|
1444
|
+
let agentOptions;
|
|
1445
|
+
if (agentCommand !== undefined) {
|
|
1446
|
+
agentOptions = {
|
|
1447
|
+
command: agentCommand,
|
|
1448
|
+
...(agentArgs.length === 0 ? {} : { args: agentArgs }),
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
if (agentCommand !== undefined && llmEnvFile !== undefined) {
|
|
1452
|
+
throw new Error('observe accepts either --dsh-agent-command or --llm-env-file, not both');
|
|
1453
|
+
}
|
|
1454
|
+
const result = await runObserver(config, previousState, {
|
|
1455
|
+
source,
|
|
1456
|
+
retryPending,
|
|
1457
|
+
...(agentOptions === undefined ? {} : {
|
|
1458
|
+
agent: (task, prompt) => runDshAgentCommand(task, prompt, agentOptions),
|
|
1459
|
+
}),
|
|
1460
|
+
...(llmEnvFile === undefined ? {} : {
|
|
1461
|
+
agent: (task, prompt) => runOpenAiCompatibleAgent(task, prompt, { envFile: resolve(llmEnvFile) }),
|
|
1462
|
+
}),
|
|
1463
|
+
});
|
|
1464
|
+
await saveObservationState(statePath, result.state);
|
|
1465
|
+
const reportJson = `${JSON.stringify(result.report, null, 2)}\n`;
|
|
1466
|
+
if (reportPath !== undefined) {
|
|
1467
|
+
await writeFile(resolve(reportPath), reportPath.endsWith('.json') ? reportJson : renderObserverReport(result.report));
|
|
1468
|
+
}
|
|
1469
|
+
process.stdout.write(json ? reportJson : renderObserverReport(result.report));
|
|
1470
|
+
if (result.report.errors.length > 0 || result.report.agent.failed > 0)
|
|
1471
|
+
return 1;
|
|
1472
|
+
return 0;
|
|
1473
|
+
}
|
|
1306
1474
|
async function runQuickstart(args) {
|
|
1307
1475
|
let directory = process.cwd();
|
|
1308
1476
|
let json = false;
|
|
@@ -1766,10 +1934,14 @@ async function main(args) {
|
|
|
1766
1934
|
return runDoctor(args.slice(1));
|
|
1767
1935
|
if (command === 'graph')
|
|
1768
1936
|
return runGraph(args.slice(1));
|
|
1937
|
+
if (command === 'profile-check')
|
|
1938
|
+
return runDshProfileCheck(args.slice(1));
|
|
1769
1939
|
if (command === 'probe')
|
|
1770
1940
|
return runProbe(args.slice(1));
|
|
1771
1941
|
if (command === 'demo')
|
|
1772
1942
|
return runDemo(args.slice(1));
|
|
1943
|
+
if (command === 'observe')
|
|
1944
|
+
return runObserve(args.slice(1));
|
|
1773
1945
|
if (command === 'benchmark')
|
|
1774
1946
|
return runBenchmark(args.slice(1));
|
|
1775
1947
|
if (command === 'radar')
|