upstream-radar 0.33.0 → 0.33.2
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 +231 -15
- package/dist/src/cli.js +175 -3
- 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 +10 -4
- 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 +214 -15
- 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,10 +52,12 @@ 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
|
-
| Review one exact published artifact | `upstream-radar inspect
|
|
60
|
+
| Review one exact published artifact | `upstream-radar inspect <package>@<exact-version> --deep` | Package, dependency, vulnerability, and provenance evidence for one release. |
|
|
58
61
|
| 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. |
|
|
59
62
|
| 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. |
|
|
60
63
|
|
|
@@ -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.2 inspect 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.2 inspect \
|
|
104
|
+
@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,177 @@ 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
|
+
npx --yes upstream-radar@0.33.2 observe \
|
|
274
|
+
./targets.yml \
|
|
275
|
+
--state ./observations.json \
|
|
276
|
+
--report ./upstream-radar-observer.md
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
This uses the published CLI; pin the exact version in CI rather than relying on
|
|
280
|
+
`latest`. The first cycle only creates a baseline. Later cycles compare:
|
|
281
|
+
|
|
282
|
+
- the source commit and changed files;
|
|
283
|
+
- the published npm version and integrity value;
|
|
284
|
+
- the package entrypoint, exports, Node requirement, DSH bundle metadata and dependency declarations;
|
|
285
|
+
- the real npm or pnpm lockfile graph, when a lockfile is configured.
|
|
286
|
+
|
|
287
|
+
README/docs/tests-only changes advance the observation point without waking the
|
|
288
|
+
Agent. Runtime source, DSH bundle, package entry, dependency graph, npm version,
|
|
289
|
+
or npm integrity changes create an old → new task. If the Agent is not configured,
|
|
290
|
+
the task stays in `observations.json`; no plugin is installed or executed.
|
|
291
|
+
|
|
292
|
+
If you do not have a DSH wrapper configured yet, you can point the observer at
|
|
293
|
+
an existing issue-locator/OpenAI-compatible `.env` file instead:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
upstream-radar observe ./targets.yml \
|
|
297
|
+
--state ./observations.json \
|
|
298
|
+
--llm-env-file /path/to/issue-locator/.env
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Radar reads only the endpoint, API key, and model name for that call. It never
|
|
302
|
+
writes the key or endpoint into the observation state or report. The model is
|
|
303
|
+
called only after a meaningful upstream change; a baseline or docs-only change
|
|
304
|
+
does not call it. If the endpoint is unavailable, the deterministic change
|
|
305
|
+
record remains pending and can be retried with `--retry-pending`.
|
|
306
|
+
|
|
307
|
+
The env file may use the issue-locator names (`ISSUE_LOCATOR_LLM_*`), the common
|
|
308
|
+
OpenAI names (`OPENAI_BASE_URL`, `OPENAI_API_KEY`, `OPENAI_MODEL`), or `MODEL` /
|
|
309
|
+
`CODEX_MODEL` for the model name.
|
|
310
|
+
For ModelBest-style URLs, a 404 on `/llm/v1` also retries `/llm/openai/v1`.
|
|
311
|
+
|
|
312
|
+
The scheduled workflow is [examples/github-actions/upstream-observer.yml](examples/github-actions/upstream-observer.yml).
|
|
313
|
+
The checked-in workflow is a dogfood workflow for this repository: it checks out
|
|
314
|
+
and builds Radar before running the observer. It persists only the observation
|
|
315
|
+
point. A quiet run does not create a daily commit.
|
|
316
|
+
|
|
317
|
+
The workflow also supports three optional repository secrets—
|
|
318
|
+
`ISSUE_LOCATOR_LLM_BASE_URL`, `ISSUE_LOCATOR_LLM_API_KEY`, and
|
|
319
|
+
`ISSUE_LOCATOR_LLM_MODEL`. When all three exist, the job sends meaningful tasks
|
|
320
|
+
to the issue-locator/OpenAI-compatible model. When they do not exist, static
|
|
321
|
+
upstream observation still runs and the job does not pretend that model analysis
|
|
322
|
+
was performed.
|
|
323
|
+
|
|
324
|
+
### The DSH Agent boundary
|
|
325
|
+
|
|
326
|
+
The observer accepts an explicit executable through `--dsh-agent-command`. It
|
|
327
|
+
writes one bounded, read-only task prompt to stdin and expects one JSON conclusion
|
|
328
|
+
on stdout. The command is started without a shell, and the prompt treats every
|
|
329
|
+
remote repository string and release field as untrusted evidence.
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
upstream-radar observe ./targets.yml \
|
|
333
|
+
--state ./observations.json \
|
|
334
|
+
--dsh-agent-command /path/to/reviewed-dsh-agent-wrapper \
|
|
335
|
+
--dsh-agent-arg --json
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Radar does not guess an undocumented `dsh` CLI subcommand. A reviewed DSH
|
|
339
|
+
headless wrapper is the integration boundary; this keeps the observer usable in
|
|
340
|
+
GitHub Actions and lets the DSH adapter evolve without changing observation or
|
|
341
|
+
diff logic. Use `--retry-pending` to deliver tasks left by a previous run.
|
|
342
|
+
|
|
147
343
|
## Notify Feishu or an HTTPS endpoint
|
|
148
344
|
|
|
149
345
|
To also notify a team-owned HTTPS endpoint when an incident changes, keep the endpoint outside the reviewed config and state:
|
|
@@ -322,7 +518,7 @@ cd my-dsh-plugin
|
|
|
322
518
|
pnpm install --ignore-scripts
|
|
323
519
|
|
|
324
520
|
# Read the exact graph before adding the plugin to a DSH profile.
|
|
325
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
521
|
+
pnpm dlx --package=upstream-radar@0.33.2 upstream-radar graph pnpm-lock pnpm-lock.yaml --json
|
|
326
522
|
```
|
|
327
523
|
|
|
328
524
|
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 +540,7 @@ jobs:
|
|
|
344
540
|
runs-on: ubuntu-latest
|
|
345
541
|
steps:
|
|
346
542
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
347
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
543
|
+
- uses: MicroMilo/upstream-radar@v0.33.2
|
|
348
544
|
with:
|
|
349
545
|
fail-on: high
|
|
350
546
|
fail-on-compatibility: breaking
|
|
@@ -352,6 +548,22 @@ jobs:
|
|
|
352
548
|
|
|
353
549
|
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
550
|
|
|
551
|
+
To check a real published DSH artifact directly, run one command:
|
|
552
|
+
|
|
553
|
+
```bash
|
|
554
|
+
npx --yes upstream-radar@0.33.2 inspect dsh-feishu-bot@0.15.4 --deep
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
The checked result is `REVIEW`: registry integrity, signature, provenance, and
|
|
558
|
+
89 resolved packages are verified; known vulnerabilities are `0`, while 12
|
|
559
|
+
optional dependency edges remain unresolved. That is a useful author result:
|
|
560
|
+
the empty vulnerability list is visible, but it is not mislabeled as a full
|
|
561
|
+
`ALLOW` decision.
|
|
562
|
+
|
|
563
|
+
The same exact tarball loads in both DSH `0.1.0-rc.6` and `0.1.0-rc.7` disposable
|
|
564
|
+
profiles. See the [real compatibility probe](examples/dsh/reports/dsh-feishu-bot-0.15.4-probe.md)
|
|
565
|
+
for the command and boundary.
|
|
566
|
+
|
|
355
567
|
## See one incident
|
|
356
568
|
|
|
357
569
|
If an advisory affects only one of two installed `parser` versions, Radar reports the path that actually matched:
|
|
@@ -483,7 +695,11 @@ To demonstrate the host-runtime dependency path specifically, run `pnpm run show
|
|
|
483
695
|
|
|
484
696
|
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
697
|
|
|
486
|
-
To validate the actual first-use path against
|
|
698
|
+
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.
|
|
699
|
+
|
|
700
|
+
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.
|
|
701
|
+
|
|
702
|
+
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
703
|
|
|
488
704
|
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
705
|
|
|
@@ -492,7 +708,7 @@ To see the two-source vulnerability contract without contacting the network, run
|
|
|
492
708
|
Before wiring a project into a compatibility gate, run the offline rule benchmark:
|
|
493
709
|
|
|
494
710
|
```bash
|
|
495
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
711
|
+
pnpm dlx --package=upstream-radar@0.33.2 upstream-radar benchmark compatibility
|
|
496
712
|
```
|
|
497
713
|
|
|
498
714
|
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 +721,7 @@ When you have an exact plugin artifact and want to know whether one exact DSH re
|
|
|
505
721
|
# Pack an exact npm release without running its lifecycle scripts.
|
|
506
722
|
npm pack --ignore-scripts dsh-plugin@1.2.3
|
|
507
723
|
|
|
508
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
724
|
+
pnpm dlx --package=upstream-radar@0.33.2 upstream-radar probe dsh-load \
|
|
509
725
|
./dsh-plugin-1.2.3.tgz \
|
|
510
726
|
--dsh-version 0.1.0-rc.6
|
|
511
727
|
```
|
|
@@ -531,7 +747,7 @@ It exercises a loadable bundle, a bundle patch DSH rejects, and a package that r
|
|
|
531
747
|
To compare a plugin against more than one DSH release, use the matrix form:
|
|
532
748
|
|
|
533
749
|
```bash
|
|
534
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
750
|
+
pnpm dlx --package=upstream-radar@0.33.2 upstream-radar probe dsh-matrix \
|
|
535
751
|
./dsh-plugin-1.2.3.tgz \
|
|
536
752
|
--dsh-version 0.1.0-rc.3 \
|
|
537
753
|
--dsh-version 0.1.0-rc.6 \
|
|
@@ -547,7 +763,7 @@ If your team wants the shortest scheduled CI gate before wiring a machine to a l
|
|
|
547
763
|
```yaml
|
|
548
764
|
steps:
|
|
549
765
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
550
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
766
|
+
- uses: MicroMilo/upstream-radar@v0.33.2
|
|
551
767
|
with:
|
|
552
768
|
fail-on: high
|
|
553
769
|
# Optional: also fail on deterministic DSH/plugin compatibility breaks.
|
|
@@ -556,12 +772,12 @@ steps:
|
|
|
556
772
|
threat-intel: true
|
|
557
773
|
```
|
|
558
774
|
|
|
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.
|
|
775
|
+
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.2`, and pin the checkout Action in your workflow according to your repository's policy.
|
|
560
776
|
|
|
561
777
|
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
778
|
|
|
563
779
|
```yaml
|
|
564
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
780
|
+
- uses: MicroMilo/upstream-radar@v0.33.2
|
|
565
781
|
with:
|
|
566
782
|
fail-on: high
|
|
567
783
|
```
|
|
@@ -571,7 +787,7 @@ An existing `config` wins over auto-detection. If both lockfiles exist, or neith
|
|
|
571
787
|
To review the exact plugin artifact before it enters DSH, add `inspect-package`:
|
|
572
788
|
|
|
573
789
|
```yaml
|
|
574
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
790
|
+
- uses: MicroMilo/upstream-radar@v0.33.2
|
|
575
791
|
with:
|
|
576
792
|
inspect-package: dsh-cloudflare-browser-run@0.1.1
|
|
577
793
|
# review is the safe default; use block only when incomplete coverage may pass.
|
|
@@ -583,7 +799,7 @@ This downloads that exact npm tarball, verifies the registry integrity/signature
|
|
|
583
799
|
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
800
|
|
|
585
801
|
```yaml
|
|
586
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
802
|
+
- uses: MicroMilo/upstream-radar@v0.33.2
|
|
587
803
|
with:
|
|
588
804
|
pnpm-lock: pnpm-lock.yaml
|
|
589
805
|
fail-on: high
|
|
@@ -597,7 +813,7 @@ See the [copyable npm workflow](examples/github-actions/upstream-radar-npm.yml)
|
|
|
597
813
|
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
814
|
|
|
599
815
|
```bash
|
|
600
|
-
pnpm dlx --package=upstream-radar@0.33.
|
|
816
|
+
pnpm dlx --package=upstream-radar@0.33.2 upstream-radar radar check \
|
|
601
817
|
./upstream-radar.config.json --frozen --state :memory: --fail-on high \
|
|
602
818
|
--fail-on-compatibility breaking --json
|
|
603
819
|
```
|
|
@@ -605,7 +821,7 @@ pnpm dlx --package=upstream-radar@0.33.0 upstream-radar radar check \
|
|
|
605
821
|
To add the optional DSH load matrix for a published plugin, provide an exact npm package and at least two exact DSH versions:
|
|
606
822
|
|
|
607
823
|
```yaml
|
|
608
|
-
- uses: MicroMilo/upstream-radar@v0.33.
|
|
824
|
+
- uses: MicroMilo/upstream-radar@v0.33.2
|
|
609
825
|
id: radar
|
|
610
826
|
with:
|
|
611
827
|
config: upstream-radar.config.json
|
|
@@ -749,7 +965,7 @@ The bounded pre-install scanner remains available as a supporting collector:
|
|
|
749
965
|
|
|
750
966
|
```bash
|
|
751
967
|
pnpm dlx --package=upstream-radar@latest upstream-radar scan /path/to/dsh-plugin
|
|
752
|
-
pnpm dlx --package=upstream-radar@latest upstream-radar inspect
|
|
968
|
+
pnpm dlx --package=upstream-radar@latest upstream-radar inspect dsh-cloudflare-browser-run@0.1.1 --deep
|
|
753
969
|
```
|
|
754
970
|
|
|
755
971
|
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`.
|
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']);
|
|
@@ -177,7 +179,7 @@ are recorded as bounded evidence.
|
|
|
177
179
|
inspect: `Upstream Radar — inspect one exact npm artifact before installation
|
|
178
180
|
|
|
179
181
|
Usage:
|
|
180
|
-
upstream-radar inspect npm
|
|
182
|
+
upstream-radar inspect [npm:]<package>@<exact-version> [--deep] [--json]
|
|
181
183
|
[--registry <https-url>] [--fail-on <warn|review|block|never>]
|
|
182
184
|
|
|
183
185
|
--deep downloads the exact tarball, verifies npm integrity/signatures and
|
|
@@ -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
|
|
|
@@ -370,8 +404,10 @@ Usage:
|
|
|
370
404
|
upstream-radar init --npm-lock <package-lock.json> [--root <package>@<exact-version>] [options]
|
|
371
405
|
upstream-radar doctor [config.json] [options]
|
|
372
406
|
upstream-radar scan <directory> [--json] [--fail-on <warn|review|block|never>]
|
|
373
|
-
upstream-radar inspect npm
|
|
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')
|