upstream-radar 0.37.0 → 0.39.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.
- package/README.md +78 -12
- package/dist/src/cli.js +94 -4
- package/dist/src/cli.js.map +1 -1
- package/dist/src/dsh-install-observation.d.ts +153 -0
- package/dist/src/dsh-install-observation.d.ts.map +1 -0
- package/dist/src/dsh-install-observation.js +856 -0
- package/dist/src/dsh-install-observation.js.map +1 -0
- package/dist/src/dsh-install-plan.d.ts +28 -0
- package/dist/src/dsh-install-plan.d.ts.map +1 -0
- package/dist/src/dsh-install-plan.js +184 -0
- package/dist/src/dsh-install-plan.js.map +1 -0
- package/dist/src/finding-watch.d.ts +31 -0
- package/dist/src/finding-watch.d.ts.map +1 -0
- package/dist/src/finding-watch.js +70 -0
- package/dist/src/finding-watch.js.map +1 -0
- 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/upstream-observer.d.ts +3 -0
- package/dist/src/upstream-observer.d.ts.map +1 -1
- package/dist/src/upstream-observer.js +60 -15
- package/dist/src/upstream-observer.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/docs/README.zh-CN.md +39 -20
- package/package.json +3 -2
- package/schemas/dsh-install-observation.schema.json +217 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Upstream Radar is not another package-name vulnerability scanner. It follows one
|
|
|
10
10
|
|
|
11
11
|
| Product job | What Radar establishes |
|
|
12
12
|
| --- | --- |
|
|
13
|
-
| **DSH compatibility / admission** | Whether the exact published bundle can be registered and loaded by the DSH releases you care about. |
|
|
13
|
+
| **DSH compatibility / admission** | Whether the exact published bundle can be installed, registered, and loaded by the DSH releases you care about, with an isolated execution lane for behavior evidence. |
|
|
14
14
|
| **Real dependency graph** | Which exact package versions and physical paths the plugin brings into the DSH profile, including unresolved edges. |
|
|
15
15
|
| **Continuous upstream monitoring** | Whether an advisory, npm release, DSH/Cordis change, or breaking signal changes the old → new situation. |
|
|
16
16
|
| **Author-facing repair** | Which plugin, dependency path, version, lockfile, or DSH declaration gives the author a concrete next fix. |
|
|
@@ -21,7 +21,7 @@ The deterministic scanner establishes package, graph, advisory, and compatibilit
|
|
|
21
21
|
|
|
22
22
|
```mermaid
|
|
23
23
|
flowchart TD
|
|
24
|
-
A["DSH plugin source or exact npm artifact"] --> B["DSH compatibility
|
|
24
|
+
A["DSH plugin source or exact npm artifact"] --> B["Static review + DSH compatibility check"]
|
|
25
25
|
B --> C["Build the real plugin → dependency graph"]
|
|
26
26
|
C --> D["Monitor advisories, npm, DSH and Cordis changes"]
|
|
27
27
|
D --> E{"Meaningful affected change?"}
|
|
@@ -63,26 +63,67 @@ baseline report, and defined in [`schemas/upstream-downstream-ir.schema.json`](s
|
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
65
|
# No DSH profile, API key, or network state required
|
|
66
|
-
npx --yes upstream-radar@0.
|
|
66
|
+
npx --yes upstream-radar@0.39.0 demo
|
|
67
67
|
|
|
68
68
|
# Scan a public DSH plugin repository without installing it
|
|
69
|
-
npx --yes upstream-radar@0.
|
|
69
|
+
npx --yes upstream-radar@0.39.0 scan \
|
|
70
70
|
https://github.com/PlutoKeating/dsh-lark-bot \
|
|
71
71
|
--fail-on never
|
|
72
72
|
|
|
73
73
|
# Review a real browser plugin users would install, then check two DSH releases
|
|
74
|
-
npx --yes upstream-radar@0.
|
|
74
|
+
npx --yes upstream-radar@0.39.0 review dsh-plugin dsh-cloudflare-browser-run@0.1.1 \
|
|
75
75
|
--dsh-version 0.1.0-rc.6,0.1.0-rc.7
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
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.
|
|
79
79
|
|
|
80
|
+
## The isolated execution lane
|
|
81
|
+
|
|
82
|
+
Static metadata tells us that a lifecycle script exists; it cannot tell us what
|
|
83
|
+
actually ran. The new [`dsh-install` workflow](.github/workflows/observe-dsh-plugin-install.yml)
|
|
84
|
+
therefore uses a separate, deliberately untrusted lane:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
fresh GitHub-hosted VM (no secrets, read-only repository token)
|
|
88
|
+
→ restricted container (no host workspace or Docker socket)
|
|
89
|
+
→ npm pack exact package@version with scripts disabled
|
|
90
|
+
→ record the exact Node and pnpm runtime used by the check
|
|
91
|
+
→ DSH installs that same tarball with only the declared dependency-build approvals
|
|
92
|
+
→ strace records child processes, network destinations and file writes
|
|
93
|
+
→ DSH loads the registered bundle under the same exact release
|
|
94
|
+
→ bounded JSON report survives and a non-compatible pair fails the check
|
|
95
|
+
→ the container and VM are discarded
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Use **Actions → Observe one DSH plugin install** and supply one exact plugin and
|
|
99
|
+
one exact DSH version. The CLI also exposes `probe dsh-install`, but it refuses
|
|
100
|
+
to run unless both `--execute` and `UPSTREAM_RADAR_ISOLATED_RUNNER=1` are present;
|
|
101
|
+
it is not intended as a normal laptop command.
|
|
102
|
+
|
|
103
|
+
This lane is wired into the always-on observer. Radar now watches the official
|
|
104
|
+
`@deepseek-ai/dsh` `next` release channel (current DSH releases are prereleases
|
|
105
|
+
rather than npm `latest`). A new exact DSH publication fans out the small
|
|
106
|
+
[maintained plugin corpus](examples/dsh/install-observer/targets.json), one fresh
|
|
107
|
+
VM per plugin. A mapped plugin publication retests only that plugin. Unchanged
|
|
108
|
+
evidence keeps `observations.json` byte-stable, persistent source/publish drift
|
|
109
|
+
does not wake the Agent again, and the DSH Agent/API key never enters the execution job.
|
|
110
|
+
Build-script approvals are exact package names stored in the maintained target
|
|
111
|
+
and copied into every report; an absent list approves nothing.
|
|
112
|
+
|
|
113
|
+
The first implementation is intentionally honest about its limit: a container
|
|
114
|
+
shares the hosted VM's kernel, and same-container `strace` evidence is not
|
|
115
|
+
tamper-proof against determined malicious code. The outer VM is disposable and
|
|
116
|
+
secret-free; a Firecracker collector is the later high-assurance backend, not a
|
|
117
|
+
claim made by this report. See the [execution boundary and result semantics](examples/dsh/install-observer/README.md)
|
|
118
|
+
and [`dsh-install-observation.schema.json`](schemas/dsh-install-observation.schema.json).
|
|
119
|
+
|
|
80
120
|
## What we have already found
|
|
81
121
|
|
|
82
122
|
These are real, reproducible cases in this repository—not synthetic “vulnerable package” demos.
|
|
83
123
|
|
|
84
124
|
| Case | Finding | Why it matters |
|
|
85
125
|
| --- | --- | --- |
|
|
126
|
+
| [Live DSH `0.1.1-rc.1` isolated matrix](examples/dsh/install-observer/reports/2026-08-21-dsh-0.1.1-rc.1.md) | All three maintained plugins install/register/load under their recorded contracts; Feishu's raw control fails until its documented `protobufjs` approval is supplied | This is the first always-on behavior result: Radar preserves a red negative control without mislabeling a documented install policy as a new author defect. |
|
|
86
127
|
| [`dsh-cloudflare-browser-run@0.1.1`](examples/reports/dsh-cloudflare-browser-run-0.1.1.txt) | 18 resolved packages, 2 unresolved optional Cordis edges, 0 known vulnerabilities, and DSH rc.6/rc.7 both loaded the bundle | A real browser plugin demonstrates the DSH admission boundary and why incomplete edges stay visible. |
|
|
87
128
|
| [50-plugin batch](examples/dsh/reports/dsh-batch-50-2026-08-17.md) / [real graph corpus](examples/dsh/first-batch/README.md) | 50 source scans, 30 exact npm reviews, 37 real plugin graphs indexed; 13 targets kept as missing evidence | The reverse index is now built from real DSH plugins, and missing graphs are not treated as clean. |
|
|
88
129
|
| [`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.” |
|
|
@@ -91,6 +132,30 @@ These are real, reproducible cases in this repository—not synthetic “vulnera
|
|
|
91
132
|
|
|
92
133
|
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.
|
|
93
134
|
|
|
135
|
+
## Monitor the findings we already found
|
|
136
|
+
|
|
137
|
+
The repository now has a focused watch for seven real DSH plugins from the first
|
|
138
|
+
batch. It re-runs the source scan and the exact npm artifact review, then stores
|
|
139
|
+
only trusted observations in [`state.json`](examples/dsh/finding-watch/state.json)
|
|
140
|
+
and writes the current author-facing result to
|
|
141
|
+
[`report.md`](examples/dsh/finding-watch/report.md).
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pnpm run monitor:dsh-findings
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The watch distinguishes `persisting`, `added`, `resolved`, `changed`, and
|
|
148
|
+
`unknown`. A failed registry request never becomes “resolved”; a source fix also
|
|
149
|
+
does not erase a finding that remains in the published npm artifact. The same
|
|
150
|
+
loop runs daily in [the dedicated GitHub Actions workflow](.github/workflows/dsh-finding-watch.yml)
|
|
151
|
+
and commits the state only when a trusted observation changes. It does not install
|
|
152
|
+
plugins, execute lifecycle scripts, load DSH, or call an LLM.
|
|
153
|
+
|
|
154
|
+
The current run is already useful: `dsh-msg-hub@0.1.8` has removed the old source
|
|
155
|
+
lockfile findings, but its npm artifact still reaches `protobufjs@7.6.5` with a
|
|
156
|
+
`postinstall`; `dsh-wsl-workspace` now resolves `koffi@3.1.6`, while the native
|
|
157
|
+
install step remains. The other reviewed install/lifecycle findings persist.
|
|
158
|
+
|
|
94
159
|
## The dependency graph behind every alert
|
|
95
160
|
|
|
96
161
|
```text
|
|
@@ -107,11 +172,11 @@ Two copies of `parser` are different nodes. An alert names the exact version and
|
|
|
107
172
|
For a collection of saved reports, build the reverse index that turns an upstream package update into affected plugins:
|
|
108
173
|
|
|
109
174
|
```bash
|
|
110
|
-
npx --yes upstream-radar@0.
|
|
175
|
+
npx --yes upstream-radar@0.39.0 graph reverse ./reports \
|
|
111
176
|
--output reverse-dependency-index.json
|
|
112
177
|
|
|
113
178
|
# Ask: which plugins currently depend on this exact package?
|
|
114
|
-
npx --yes upstream-radar@0.
|
|
179
|
+
npx --yes upstream-radar@0.39.0 graph reverse ./reports \
|
|
115
180
|
--package parser@2.9.0
|
|
116
181
|
|
|
117
182
|
# Rebuild the checked-in index from the real first 50 DSH plugin reports
|
|
@@ -130,7 +195,7 @@ To route an upstream old → new change to that index, pass it to the always-on
|
|
|
130
195
|
observer:
|
|
131
196
|
|
|
132
197
|
```bash
|
|
133
|
-
npx --yes upstream-radar@0.
|
|
198
|
+
npx --yes upstream-radar@0.39.0 observe ./targets.yml \
|
|
134
199
|
--reverse-index ./reverse-dependency-index.json \
|
|
135
200
|
--state ./observations.json \
|
|
136
201
|
--report ./upstream-radar-observer.md
|
|
@@ -156,7 +221,7 @@ replay baseline → one Agent task → quiet run without network access.
|
|
|
156
221
|
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.
|
|
157
222
|
|
|
158
223
|
```yaml
|
|
159
|
-
- uses: MicroMilo/upstream-radar@v0.
|
|
224
|
+
- uses: MicroMilo/upstream-radar@v0.39.0
|
|
160
225
|
with:
|
|
161
226
|
config: upstream-radar.config.json
|
|
162
227
|
fail-on: high
|
|
@@ -170,8 +235,9 @@ See the [consumer workflow](examples/github-actions/consumer/README.md) for conf
|
|
|
170
235
|
| --- | --- |
|
|
171
236
|
| Reconstruct exact npm/pnpm dependency paths | An empty finding list is a safety certificate |
|
|
172
237
|
| Query OSV and GitHub Advisory evidence for exact versions | A missing provenance statement proves maliciousness |
|
|
173
|
-
| Compare source and published artifact evidence | Static review replaces
|
|
174
|
-
|
|
|
238
|
+
| Compare source and published artifact evidence | Static review replaces runtime evidence |
|
|
239
|
+
| Observe exact install/load behavior in a disposable VM and restricted container | One observed run proves adversarial code is safe |
|
|
240
|
+
| Check DSH bundle/profile compatibility without business actions | “Compatible” means the plugin is secure |
|
|
175
241
|
| Monitor old → new upstream observations | An LLM can repair evidence that was never collected |
|
|
176
242
|
|
|
177
243
|
## Install and connect to DSH
|
|
@@ -180,7 +246,7 @@ See the [consumer workflow](examples/github-actions/consumer/README.md) for conf
|
|
|
180
246
|
pnpm add upstream-radar
|
|
181
247
|
|
|
182
248
|
# Generate a reviewable DSH profile inventory from the installed profile
|
|
183
|
-
npx --yes upstream-radar@0.
|
|
249
|
+
npx --yes upstream-radar@0.39.0 setup
|
|
184
250
|
```
|
|
185
251
|
|
|
186
252
|
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.
|
package/dist/src/cli.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
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, readdir, stat, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { access, mkdir, readFile, readdir, stat, 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
|
+
import { observeDshPluginInstall, renderDshInstallObservation, } from './dsh-install-observation.js';
|
|
9
10
|
import { renderDshPluginReview, reviewDshPlugin } from './dsh-review.js';
|
|
10
11
|
import { createAnalysisTask, renderAgentAnalysisPrompt } from './dsh-analysis.js';
|
|
11
12
|
import { createDshCaseReport, renderDshCase } from './dsh-case.js';
|
|
@@ -271,9 +272,16 @@ third-party bundles is selected automatically.
|
|
|
271
272
|
Usage:
|
|
272
273
|
upstream-radar probe dsh-load <package.tgz> [--dsh-version <exact-version>] [--json]
|
|
273
274
|
upstream-radar probe dsh-matrix <package.tgz> --dsh-version <v1>,<v2>,... [--json]
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
upstream-radar probe dsh-install [npm:]<package>@<exact-version>
|
|
276
|
+
--dsh-version <exact-version> --isolation-provider <provider> --execute
|
|
277
|
+
[--allow-build <package>]... [--timeout <seconds>] [--report <report.json>] [--json]
|
|
278
|
+
|
|
279
|
+
The load probes disable lifecycle scripts and check bundle registration/load.
|
|
280
|
+
The dsh-install probe deliberately executes the exact plugin's lifecycle scripts
|
|
281
|
+
and loads the bundle while recording Linux process, network, and file-change
|
|
282
|
+
evidence. Run it only inside a disposable, secret-free Linux environment. It
|
|
283
|
+
requires both --execute and UPSTREAM_RADAR_ISOLATED_RUNNER=1.
|
|
284
|
+
Radar records the caller's isolation-provider claim but cannot verify it.
|
|
277
285
|
`,
|
|
278
286
|
review: `Upstream Radar — review one exact published DSH plugin in one command
|
|
279
287
|
|
|
@@ -469,6 +477,7 @@ Usage:
|
|
|
469
477
|
upstream-radar profile-check [profile-directory] [--patch <path>] [--report <path>] [--summary] [--json]
|
|
470
478
|
upstream-radar probe dsh-load <package.tgz> [--dsh-version <exact-version>] [--timeout <seconds>] [--keep-profile] [--json]
|
|
471
479
|
upstream-radar probe dsh-matrix <package.tgz> --dsh-version <v1>[,<v2>,...] [--timeout <seconds>] [--keep-profile] [--json]
|
|
480
|
+
upstream-radar probe dsh-install [npm:]<package>@<exact-version> --dsh-version <exact-version> --isolation-provider <github-actions-hosted-runner|firecracker|other> --execute [--allow-build <package>]... [--timeout <seconds>] [--report <report.json>] [--json]
|
|
472
481
|
upstream-radar review dsh-plugin [npm:]<package>@<exact-version> --dsh-version <v1>,<v2>,... [--json]
|
|
473
482
|
upstream-radar demo [--json]
|
|
474
483
|
upstream-radar case dsh-web-ui [--json]
|
|
@@ -1203,6 +1212,8 @@ async function runDshCase(args) {
|
|
|
1203
1212
|
}
|
|
1204
1213
|
async function runProbe(args) {
|
|
1205
1214
|
const mode = args[0];
|
|
1215
|
+
if (mode === 'dsh-install')
|
|
1216
|
+
return runDshInstallObservation(args.slice(1));
|
|
1206
1217
|
if (mode !== 'dsh-load' && mode !== 'dsh-matrix')
|
|
1207
1218
|
throw new Error('probe requires dsh-load or dsh-matrix');
|
|
1208
1219
|
const packagePath = args[1];
|
|
@@ -1267,6 +1278,85 @@ async function runProbe(args) {
|
|
|
1267
1278
|
process.stdout.write(json ? `${JSON.stringify(report, null, 2)}\n` : renderDshLoadMatrix(report));
|
|
1268
1279
|
return report.result === 'compatible' ? 0 : report.result === 'incompatible' ? 2 : 1;
|
|
1269
1280
|
}
|
|
1281
|
+
async function runDshInstallObservation(args) {
|
|
1282
|
+
const packageSpec = args[0];
|
|
1283
|
+
if (packageSpec === undefined || packageSpec.startsWith('-')) {
|
|
1284
|
+
throw new Error('probe dsh-install requires an exact npm package');
|
|
1285
|
+
}
|
|
1286
|
+
let dshVersion;
|
|
1287
|
+
let isolationProvider;
|
|
1288
|
+
let timeoutSeconds = 180;
|
|
1289
|
+
let reportPath;
|
|
1290
|
+
const allowedBuilds = [];
|
|
1291
|
+
let execute = false;
|
|
1292
|
+
let json = false;
|
|
1293
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1294
|
+
const argument = args[index];
|
|
1295
|
+
if (argument === '--execute') {
|
|
1296
|
+
execute = true;
|
|
1297
|
+
}
|
|
1298
|
+
else if (argument === '--json') {
|
|
1299
|
+
json = true;
|
|
1300
|
+
}
|
|
1301
|
+
else if (argument === '--dsh-version' || argument === '--isolation-provider' || argument === '--allow-build' || argument === '--timeout' || argument === '--report') {
|
|
1302
|
+
const value = args[index + 1];
|
|
1303
|
+
if (value === undefined || value.startsWith('-'))
|
|
1304
|
+
throw new Error(`${argument} requires a value`);
|
|
1305
|
+
if (argument === '--dsh-version') {
|
|
1306
|
+
if (dshVersion !== undefined)
|
|
1307
|
+
throw new Error('probe dsh-install accepts only one --dsh-version');
|
|
1308
|
+
dshVersion = value;
|
|
1309
|
+
}
|
|
1310
|
+
else if (argument === '--isolation-provider') {
|
|
1311
|
+
if (value !== 'github-actions-hosted-runner' && value !== 'firecracker' && value !== 'other') {
|
|
1312
|
+
throw new Error('--isolation-provider must be github-actions-hosted-runner, firecracker or other');
|
|
1313
|
+
}
|
|
1314
|
+
isolationProvider = value;
|
|
1315
|
+
}
|
|
1316
|
+
else if (argument === '--timeout') {
|
|
1317
|
+
const parsed = Number(value);
|
|
1318
|
+
if (!Number.isSafeInteger(parsed) || parsed < 30 || parsed > 600) {
|
|
1319
|
+
throw new Error('--timeout must be an integer between 30 and 600 seconds');
|
|
1320
|
+
}
|
|
1321
|
+
timeoutSeconds = parsed;
|
|
1322
|
+
}
|
|
1323
|
+
else if (argument === '--allow-build') {
|
|
1324
|
+
allowedBuilds.push(value);
|
|
1325
|
+
}
|
|
1326
|
+
else {
|
|
1327
|
+
reportPath = value;
|
|
1328
|
+
}
|
|
1329
|
+
index += 1;
|
|
1330
|
+
}
|
|
1331
|
+
else {
|
|
1332
|
+
throw new Error(`unknown option for probe dsh-install: ${argument}`);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
if (dshVersion === undefined)
|
|
1336
|
+
throw new Error('probe dsh-install requires one exact --dsh-version');
|
|
1337
|
+
if (isolationProvider === undefined)
|
|
1338
|
+
throw new Error('probe dsh-install requires --isolation-provider');
|
|
1339
|
+
if (!execute)
|
|
1340
|
+
throw new Error('probe dsh-install requires --execute because third-party code may run');
|
|
1341
|
+
if (process.env.UPSTREAM_RADAR_ISOLATED_RUNNER !== '1') {
|
|
1342
|
+
throw new Error('probe dsh-install requires UPSTREAM_RADAR_ISOLATED_RUNNER=1 in the disposable environment');
|
|
1343
|
+
}
|
|
1344
|
+
const report = await observeDshPluginInstall({
|
|
1345
|
+
packageSpec,
|
|
1346
|
+
dshVersion,
|
|
1347
|
+
allowExecution: true,
|
|
1348
|
+
isolationProvider,
|
|
1349
|
+
allowedBuilds,
|
|
1350
|
+
timeoutMs: timeoutSeconds * 1_000,
|
|
1351
|
+
});
|
|
1352
|
+
if (reportPath !== undefined) {
|
|
1353
|
+
const absoluteReportPath = resolve(reportPath);
|
|
1354
|
+
await mkdir(dirname(absoluteReportPath), { recursive: true });
|
|
1355
|
+
await writeFile(absoluteReportPath, `${JSON.stringify(report, null, 2)}\n`, { mode: 0o600 });
|
|
1356
|
+
}
|
|
1357
|
+
process.stdout.write(json ? `${JSON.stringify(report, null, 2)}\n` : renderDshInstallObservation(report));
|
|
1358
|
+
return report.result === 'compatible' ? 0 : report.result === 'unknown' ? 1 : 2;
|
|
1359
|
+
}
|
|
1270
1360
|
async function runDshPluginReview(args) {
|
|
1271
1361
|
if (args[0] !== 'dsh-plugin')
|
|
1272
1362
|
throw new Error('review requires dsh-plugin');
|