upstream-radar 0.43.5 → 0.44.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-zh-CN.md +42 -21
- package/README.md +54 -29
- package/dist/src/cli.js +155 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/dsh-compatibility-ledger.d.ts +2 -0
- package/dist/src/dsh-compatibility-ledger.d.ts.map +1 -1
- package/dist/src/dsh-compatibility-ledger.js +5 -0
- package/dist/src/dsh-compatibility-ledger.js.map +1 -1
- package/dist/src/dsh-install-observation.d.ts +6 -0
- package/dist/src/dsh-install-observation.d.ts.map +1 -1
- package/dist/src/dsh-install-observation.js +2 -2
- package/dist/src/dsh-install-observation.js.map +1 -1
- package/dist/src/dsh-surface-observation.d.ts +160 -0
- package/dist/src/dsh-surface-observation.d.ts.map +1 -0
- package/dist/src/dsh-surface-observation.js +1031 -0
- package/dist/src/dsh-surface-observation.js.map +1 -0
- package/dist/src/dsh-surface.d.ts +127 -0
- package/dist/src/dsh-surface.d.ts.map +1 -0
- package/dist/src/dsh-surface.js +667 -0
- package/dist/src/dsh-surface.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 +4 -0
- package/dist/src/upstream-observer.d.ts.map +1 -1
- package/dist/src/upstream-observer.js +40 -4
- 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 +19 -19
- package/package.json +1 -1
package/README-zh-CN.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<h1 align="center">Upstream Radar</h1>
|
|
2
2
|
|
|
3
|
-
<p align="center"><strong
|
|
3
|
+
<p align="center"><strong>持续验证 DeepSeek Harness 插件兼容性——覆盖 headless、Web 和 TUI。</strong></p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="README.md">English</a> ·
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
<a href="LICENSE"><img alt="Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-blue.svg"></a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
-
Upstream Radar
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
Upstream Radar 把精确的插件发布物、DSH 宿主和运行环境绑定在一起,让 Agent 根据仓库说明和
|
|
14
|
+
已有失败证据推导受限环境,再放进一次性 GitHub VM 中实际验证。生态发生变化或证据过期后都会
|
|
15
|
+
重跑,因此它检查的是**当前版本是否仍然可用**,而不只是比较一次 diff。
|
|
16
16
|
|
|
17
17
|
它面向 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness) 插件生态。
|
|
18
18
|
静态检查是关于发布包的证据;隔离运行检查是关于某个精确的
|
|
@@ -38,31 +38,38 @@ Upstream Radar 持续检查三者之间的真实关系:精确的 DSH 插件发
|
|
|
38
38
|
|
|
39
39
|
## Radar 做什么
|
|
40
40
|
|
|
41
|
-
1.
|
|
42
|
-
2.
|
|
43
|
-
3.
|
|
44
|
-
4.
|
|
41
|
+
1. **建立统一兼容记录(IR)。** 对齐 npm 发布物、源码 commit、DSH 宿主、运行环境/profile、依赖路径和漏洞情报。
|
|
42
|
+
2. **推导运行环境。** Agent 读取作者声明的安装说明和失败证据,只输出边界明确的安装计划。
|
|
43
|
+
3. **验证不同执行平面。** 全新、无密钥的 runner 分别验证 headless 加载、Chromium Web 启动或真实 PTY TUI 交互。
|
|
44
|
+
4. **让结果持续有效。** DSH/插件/依赖变化以及证据过期都会触发复测;确认的问题变成可修报告,干净复测后完成闭环。
|
|
45
45
|
|
|
46
46
|
```mermaid
|
|
47
47
|
flowchart TB
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
Trigger["定时运行 / 上游变化 / 证据过期"] --> IR["精确 IR:插件字节 ↔ DSH ↔ 运行环境 ↔ 依赖"]
|
|
49
|
+
IR --> Agent["Agent 推导受限安装计划"]
|
|
50
|
+
Agent --> VM{"全新、无密钥的 GitHub VM"}
|
|
51
|
+
VM --> Headless["Headless:安装 → 注册 → 加载"]
|
|
52
|
+
VM --> Web["Web:Chromium → 启动交接 → 客户端包"]
|
|
53
|
+
VM --> TUI["TUI:PTY → 画面 → 输入 → 声明的退出方式"]
|
|
54
|
+
Headless --> Ledger["版本化证据账本 + 反向影响索引"]
|
|
55
|
+
Web --> Ledger
|
|
56
|
+
TUI --> Ledger
|
|
57
|
+
Ledger --> Decision{"能归责于插件吗?"}
|
|
58
|
+
Decision -->|"能"| Issue["生成一条可修复的维护者报告"]
|
|
59
|
+
Decision -->|"不能 / 检测器缺口"| Hold["暂扣报告并校准"]
|
|
60
|
+
Issue -->|"作者发布修复"| Trigger
|
|
61
|
+
Hold --> Trigger
|
|
54
62
|
```
|
|
55
63
|
|
|
56
|
-
Agent
|
|
57
|
-
|
|
58
|
-
虚拟机执行,也不能把缺失证据说成通过。
|
|
64
|
+
Agent 可以选择作者声明的构建包、profile 设置和下一次受限重试。精确指纹决定一份报告能填入
|
|
65
|
+
哪个测试格子;真正的结果由一次性虚拟机执行得出,而不是模型判断。缺失证据永远不能变成通过。
|
|
59
66
|
|
|
60
67
|
## 试试一个真实检查
|
|
61
68
|
|
|
62
69
|
第一次检查不需要本地 DSH profile,也不会执行插件代码:
|
|
63
70
|
|
|
64
71
|
```bash
|
|
65
|
-
npx --yes upstream-radar@0.
|
|
72
|
+
npx --yes upstream-radar@0.44.0 inspect \
|
|
66
73
|
@sanqi-normal/dsh-webui-market-plugin@0.5.4 \
|
|
67
74
|
--deep --fail-on never
|
|
68
75
|
```
|
|
@@ -73,7 +80,7 @@ npx --yes upstream-radar@0.43.5 inspect \
|
|
|
73
80
|
如果要检查自己的公开仓库,而不安装它:
|
|
74
81
|
|
|
75
82
|
```bash
|
|
76
|
-
npx --yes upstream-radar@0.
|
|
83
|
+
npx --yes upstream-radar@0.44.0 scan \
|
|
77
84
|
https://github.com/owner/dsh-plugin \
|
|
78
85
|
--fail-on never
|
|
79
86
|
```
|
|
@@ -88,8 +95,9 @@ npx --yes upstream-radar@0.43.5 scan \
|
|
|
88
95
|
- [每天观察一个插件仓库](examples/github-actions/upstream-observer-minimal.yml):比较 commit、发布版本、manifest 和依赖图,只有发生重要变化才唤起 Agent。
|
|
89
96
|
- [在 CI 中运行依赖门禁](examples/github-actions/upstream-radar.yml):在合并前检查 lockfile 或审查过的 Radar 配置。
|
|
90
97
|
|
|
91
|
-
[
|
|
92
|
-
|
|
98
|
+
隔离的 [headless](.github/workflows/observe-dsh-plugin-install.yml) 和
|
|
99
|
+
[Web/TUI](.github/workflows/observe-dsh-plugin-surface.yml) workflow 都使用全新的 GitHub 托管 runner。
|
|
100
|
+
它们不是你的电脑,也不会接收项目或模型密钥。
|
|
93
101
|
|
|
94
102
|
## 来自真实生态的结果
|
|
95
103
|
|
|
@@ -97,6 +105,19 @@ npx --yes upstream-radar@0.43.5 scan \
|
|
|
97
105
|
通过、22 个仅待复核结果、0 个已复现不兼容和 4 个仅仓库条目。在真正观测所需的
|
|
98
106
|
Web/client 或仓库安装平面之前,待复核项不会被包装成插件故障。
|
|
99
107
|
|
|
108
|
+
首批非 headless 测试已经在 GitHub 托管 VM 中跑通:
|
|
109
|
+
|
|
110
|
+
| 精确测试格子 | 实际证据 | 结果 |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| [`dsh-univer-office@0.2.9 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32823035297/job/97726205358) | HTTP 200、DSH 启动完成交接、客户端下载成功、浏览器和页面无错误 | **兼容** |
|
|
113
|
+
| [`@deepseek-harness-tui/dsh-tui@0.9.2 × DSH 0.1.1-rc.2 × TUI`](https://github.com/MicroMilo/upstream-radar/actions/runs/32823035297/job/97726205289) | 真实 PTY 画面、键盘输入、按文档双击 Ctrl-C 后以 code 0 退出 | **兼容** |
|
|
114
|
+
| [`@linxin666/dsh-web-all@0.3.3 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32828788296/job/97742850608) | Agent 批准 4 个依赖构建;聚合客户端包返回 200;启动清单、应用挂载和插件实体相互吻合 | **兼容** |
|
|
115
|
+
|
|
116
|
+
两次首次测试都被 Radar 暂扣,没有提交给作者:TUI 检测器只发送了一次 Ctrl-C,而插件文档要求
|
|
117
|
+
连按两次;Web 检测器把 Cordis 内部加载行误当成公开 npm 模块 ID。我们修正两份测试契约,并用
|
|
118
|
+
相同精确发布物复测。聚合 Web 案例仍如实保留一个非阻塞的 `/status` 探测 404,但不会把这条可选
|
|
119
|
+
请求误判为插件启动失败。
|
|
120
|
+
|
|
100
121
|
截至 2026-08-25,Radar 共向维护者提交了 13 条报告。比数量更重要的是处理结果:
|
|
101
122
|
|
|
102
123
|
| 结果 | 报告 |
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<h1 align="center">Upstream Radar</h1>
|
|
2
2
|
|
|
3
|
-
<p align="center"><strong>
|
|
3
|
+
<p align="center"><strong>Always-on compatibility testing for DeepSeek Harness plugins—across headless, Web, and TUI.</strong></p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="README-zh-CN.md">简体中文</a> ·
|
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
<a href="LICENSE"><img alt="Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-blue.svg"></a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
-
Upstream Radar
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
Upstream Radar binds exact plugin bytes to an exact DSH host and runtime, lets an
|
|
14
|
+
Agent derive a bounded environment from repository instructions and prior
|
|
15
|
+
evidence, then proves the relationship in disposable GitHub VMs. It runs again
|
|
16
|
+
when the ecosystem changes **or evidence expires**, so it tests the current
|
|
17
|
+
version—not only the diff.
|
|
17
18
|
|
|
18
19
|
It is built for the [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness)
|
|
19
20
|
plugin ecosystem. A static review is evidence about a package; an isolated
|
|
@@ -41,30 +42,38 @@ repositories are checked separately.
|
|
|
41
42
|
|
|
42
43
|
## What Radar does
|
|
43
44
|
|
|
44
|
-
1. **
|
|
45
|
-
|
|
46
|
-
2. **
|
|
47
|
-
|
|
48
|
-
3. **
|
|
49
|
-
|
|
50
|
-
4. **
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
1. **Build one exact compatibility record (IR).** Align the npm artifact,
|
|
46
|
+
source commit, DSH host, runtime/profile, dependency paths, and advisories.
|
|
47
|
+
2. **Derive the environment.** An Agent reads declared installation guidance
|
|
48
|
+
and failed evidence, then emits only a bounded install plan.
|
|
49
|
+
3. **Prove each execution plane.** Fresh, secret-free runners exercise headless
|
|
50
|
+
load, Chromium Web boot, or a real PTY TUI interaction.
|
|
51
|
+
4. **Keep the result alive.** DSH/plugin/dependency changes and evidence expiry
|
|
52
|
+
trigger retests; confirmed failures become fixable reports and clean retests
|
|
53
|
+
close the loop.
|
|
53
54
|
|
|
54
55
|
```mermaid
|
|
55
56
|
flowchart TB
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
Trigger["Schedule / upstream change / evidence expiry"] --> IR["Exact IR: plugin bytes ↔ DSH ↔ runtime ↔ dependencies"]
|
|
58
|
+
IR --> Agent["Agent derives a bounded install plan"]
|
|
59
|
+
Agent --> VM{"Fresh secret-free GitHub VM"}
|
|
60
|
+
VM --> Headless["Headless: install → register → load"]
|
|
61
|
+
VM --> Web["Web: Chromium → boot handoff → client bundle"]
|
|
62
|
+
VM --> TUI["TUI: PTY → frame → input → declared shutdown"]
|
|
63
|
+
Headless --> Ledger["Versioned evidence ledger + reverse impact index"]
|
|
64
|
+
Web --> Ledger
|
|
65
|
+
TUI --> Ledger
|
|
66
|
+
Ledger --> Decision{"Plugin-attributable failure?"}
|
|
67
|
+
Decision -->|"yes"| Issue["Produce one fixable maintainer report"]
|
|
68
|
+
Decision -->|"no / detector gap"| Hold["Hold the report and calibrate"]
|
|
69
|
+
Issue -->|"author ships a fix"| Trigger
|
|
70
|
+
Hold --> Trigger
|
|
62
71
|
```
|
|
63
72
|
|
|
64
|
-
The Agent
|
|
65
|
-
|
|
66
|
-
the model—establishes the result.
|
|
67
|
-
|
|
73
|
+
The Agent may choose declared build packages, profile setup, and the next bounded
|
|
74
|
+
retry. Exact fingerprints decide which cell a report can satisfy, and the
|
|
75
|
+
disposable runner—not the model—establishes the result. Missing evidence can
|
|
76
|
+
never become a pass.
|
|
68
77
|
|
|
69
78
|
## Try a real check
|
|
70
79
|
|
|
@@ -72,7 +81,7 @@ No local DSH profile is needed for this first check. It reviews one exact
|
|
|
72
81
|
published artifact without executing plugin code:
|
|
73
82
|
|
|
74
83
|
```bash
|
|
75
|
-
npx --yes upstream-radar@0.
|
|
84
|
+
npx --yes upstream-radar@0.44.0 inspect \
|
|
76
85
|
@sanqi-normal/dsh-webui-market-plugin@0.5.4 \
|
|
77
86
|
--deep --fail-on never
|
|
78
87
|
```
|
|
@@ -85,7 +94,7 @@ behavior. See the [full evidence report](examples/dsh/reports/sanqi-market-plugi
|
|
|
85
94
|
To review your own public repository without installing it:
|
|
86
95
|
|
|
87
96
|
```bash
|
|
88
|
-
npx --yes upstream-radar@0.
|
|
97
|
+
npx --yes upstream-radar@0.44.0 scan \
|
|
89
98
|
https://github.com/owner/dsh-plugin \
|
|
90
99
|
--fail-on never
|
|
91
100
|
```
|
|
@@ -106,9 +115,10 @@ Copy one of the maintained workflows into your repository:
|
|
|
106
115
|
- [Run the dependency gate in CI](examples/github-actions/upstream-radar.yml)
|
|
107
116
|
— checks the lockfile or reviewed Radar configuration before merge.
|
|
108
117
|
|
|
109
|
-
The
|
|
110
|
-
|
|
111
|
-
your workstation and
|
|
118
|
+
The isolated [headless](.github/workflows/observe-dsh-plugin-install.yml) and
|
|
119
|
+
[Web/TUI](.github/workflows/observe-dsh-plugin-surface.yml) observers use fresh
|
|
120
|
+
GitHub-hosted runners. They are not your workstation and receive no project or
|
|
121
|
+
model secrets.
|
|
112
122
|
|
|
113
123
|
## Evidence from the ecosystem
|
|
114
124
|
|
|
@@ -118,6 +128,21 @@ incompatibilities, and four repository-only entries. The review-only group is
|
|
|
118
128
|
kept separate from failures until the required Web/client or repository install
|
|
119
129
|
plane is actually observed.
|
|
120
130
|
|
|
131
|
+
The first non-headless cells now run in GitHub-hosted VMs:
|
|
132
|
+
|
|
133
|
+
| Exact cell | Observed proof | Result |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| [`dsh-univer-office@0.2.9 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32823035297/job/97726205358) | HTTP 200, DSH boot handoff, declared client bundle fetched, no browser/page errors | **Compatible** |
|
|
136
|
+
| [`@deepseek-harness-tui/dsh-tui@0.9.2 × DSH 0.1.1-rc.2 × TUI`](https://github.com/MicroMilo/upstream-radar/actions/runs/32823035297/job/97726205289) | Real PTY frame, keyboard input, documented double-Ctrl-C exit, code 0 | **Compatible** |
|
|
137
|
+
| [`@linxin666/dsh-web-all@0.3.3 × DSH 0.1.1-rc.2 × Web`](https://github.com/MicroMilo/upstream-radar/actions/runs/32828788296/job/97742850608) | Agent-approved four dependency builds; aggregate client bundle returned 200; boot manifest, app mount, and plugin materialization matched | **Compatible** |
|
|
138
|
+
|
|
139
|
+
Two first attempts were held by Radar rather than reported to authors. The TUI
|
|
140
|
+
observer sent one Ctrl-C while the plugin documents a double press. The Web
|
|
141
|
+
observer confused an internal Cordis loader row with the public npm module ID.
|
|
142
|
+
Radar corrected both contracts and reran the same exact artifacts. The aggregate
|
|
143
|
+
Web case still records a non-blocking `/status` probe returning 404, without
|
|
144
|
+
mistaking that optional request for a failed plugin boot.
|
|
145
|
+
|
|
121
146
|
As of 2026-08-25, Radar has filed 13 maintainer-facing reports. The outcome is
|
|
122
147
|
more useful than the raw count:
|
|
123
148
|
|
package/dist/src/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { renderCompatibilityBenchmark, runCompatibilityBenchmark } from './compa
|
|
|
7
7
|
import { assessCompatibilityChange } from './compatibility.js';
|
|
8
8
|
import { probeDshLoad, probeDshLoadMatrix, renderDshLoadMatrix, renderDshLoadProbe } from './dsh-probe.js';
|
|
9
9
|
import { observeDshPluginInstall, renderDshInstallObservation, } from './dsh-install-observation.js';
|
|
10
|
+
import { observeDshPluginSurface, renderDshSurfaceObservation, } from './dsh-surface-observation.js';
|
|
10
11
|
import { renderDshPluginReview, reviewDshPlugin } from './dsh-review.js';
|
|
11
12
|
import { createAnalysisTask, renderAgentAnalysisPrompt } from './dsh-analysis.js';
|
|
12
13
|
import { createDshCaseReport, renderDshCase } from './dsh-case.js';
|
|
@@ -281,6 +282,14 @@ Usage:
|
|
|
281
282
|
upstream-radar probe dsh-install [npm:]<package>@<exact-version>
|
|
282
283
|
--dsh-version <exact-version> --isolation-provider <provider> --execute
|
|
283
284
|
[--allow-build <package>]... [--timeout <seconds>] [--report <report.json>] [--json]
|
|
285
|
+
upstream-radar probe dsh-surface [npm:]<package>@<exact-version>
|
|
286
|
+
--dsh-version <exact-version> --case-id <id> --source-case-id <id>
|
|
287
|
+
--source-fingerprint <sha256:...> --contract-fingerprint <sha256:...>
|
|
288
|
+
--plane <web|tui> --profile <name> --runtime-id <id>
|
|
289
|
+
--artifact-sha256 <hex> --isolation-provider <provider> --execute
|
|
290
|
+
[--allow-build <package>]...
|
|
291
|
+
[--driver-root <path>] [--chromium-executable <path>]
|
|
292
|
+
[--artifacts <directory>] [--timeout <seconds>] [--report <report.json>] [--json]
|
|
284
293
|
|
|
285
294
|
The load probes disable lifecycle scripts and check bundle registration/load.
|
|
286
295
|
The dsh-install probe deliberately executes the exact plugin's lifecycle scripts
|
|
@@ -288,6 +297,10 @@ and loads the bundle while recording Linux process, network, and file-change
|
|
|
288
297
|
evidence. Run it only inside a disposable, secret-free Linux environment. It
|
|
289
298
|
requires both --execute and UPSTREAM_RADAR_ISOLATED_RUNNER=1.
|
|
290
299
|
Radar records the caller's isolation-provider claim but cannot verify it.
|
|
300
|
+
The dsh-surface probe adds plane-specific proof: Chromium must observe DSH hand
|
|
301
|
+
off from its boot page after activating the client graph, or a real PTY must observe a
|
|
302
|
+
TUI frame, bounded input, and controlled shutdown. It has the same disposable,
|
|
303
|
+
secret-free execution requirement.
|
|
291
304
|
`,
|
|
292
305
|
review: `Upstream Radar — review one exact published DSH plugin in one command
|
|
293
306
|
|
|
@@ -484,6 +497,7 @@ Usage:
|
|
|
484
497
|
upstream-radar probe dsh-load <package.tgz> [--dsh-version <exact-version>] [--timeout <seconds>] [--keep-profile] [--json]
|
|
485
498
|
upstream-radar probe dsh-matrix <package.tgz> --dsh-version <v1>[,<v2>,...] [--timeout <seconds>] [--keep-profile] [--json]
|
|
486
499
|
upstream-radar probe dsh-install [npm:]<package>@<exact-version> --dsh-version <exact-version> [--case-id <stable-label>] --isolation-provider <github-actions-hosted-runner|firecracker|other> --execute [--allow-build <package>]... [--timeout <seconds>] [--report <report.json>] [--json]
|
|
500
|
+
upstream-radar probe dsh-surface [npm:]<package>@<exact-version> --dsh-version <exact-version> --case-id <stable-label> --source-case-id <stable-label> --source-fingerprint <sha256:...> --contract-fingerprint <sha256:...> --plane <web|tui> --profile <name> --runtime-id <id> --artifact-sha256 <hex> --isolation-provider <github-actions-hosted-runner|firecracker|other> --execute [--allow-build <package>]... [--driver-root <path>] [--chromium-executable <path>] [--artifacts <directory>] [--timeout <seconds>] [--report <report.json>] [--json]
|
|
487
501
|
upstream-radar review dsh-plugin [npm:]<package>@<exact-version> --dsh-version <v1>,<v2>,... [--json]
|
|
488
502
|
upstream-radar demo [--json]
|
|
489
503
|
upstream-radar case dsh-web-ui [--json]
|
|
@@ -1220,8 +1234,10 @@ async function runProbe(args) {
|
|
|
1220
1234
|
const mode = args[0];
|
|
1221
1235
|
if (mode === 'dsh-install')
|
|
1222
1236
|
return runDshInstallObservation(args.slice(1));
|
|
1237
|
+
if (mode === 'dsh-surface')
|
|
1238
|
+
return runDshSurfaceObservation(args.slice(1));
|
|
1223
1239
|
if (mode !== 'dsh-load' && mode !== 'dsh-matrix')
|
|
1224
|
-
throw new Error('probe requires dsh-load or dsh-
|
|
1240
|
+
throw new Error('probe requires dsh-load, dsh-matrix, dsh-install or dsh-surface');
|
|
1225
1241
|
const packagePath = args[1];
|
|
1226
1242
|
if (packagePath === undefined || packagePath.startsWith('-'))
|
|
1227
1243
|
throw new Error(`probe ${mode} requires a package.tgz file`);
|
|
@@ -1372,6 +1388,144 @@ async function runDshInstallObservation(args) {
|
|
|
1372
1388
|
process.stdout.write(json ? `${JSON.stringify(report, null, 2)}\n` : renderDshInstallObservation(report));
|
|
1373
1389
|
return report.result === 'compatible' ? 0 : report.result === 'unknown' ? 1 : 2;
|
|
1374
1390
|
}
|
|
1391
|
+
async function runDshSurfaceObservation(args) {
|
|
1392
|
+
const packageSpec = args[0];
|
|
1393
|
+
if (packageSpec === undefined || packageSpec.startsWith('-')) {
|
|
1394
|
+
throw new Error('probe dsh-surface requires an exact npm package');
|
|
1395
|
+
}
|
|
1396
|
+
let dshVersion;
|
|
1397
|
+
let caseId;
|
|
1398
|
+
let sourceCaseId;
|
|
1399
|
+
let sourceFingerprint;
|
|
1400
|
+
let contractFingerprint;
|
|
1401
|
+
let plane;
|
|
1402
|
+
let profile;
|
|
1403
|
+
let runtimeId;
|
|
1404
|
+
let artifactSha256;
|
|
1405
|
+
let isolationProvider;
|
|
1406
|
+
let timeoutSeconds = 300;
|
|
1407
|
+
let reportPath;
|
|
1408
|
+
let artifactsDirectory;
|
|
1409
|
+
let driverRoot;
|
|
1410
|
+
let chromiumExecutable;
|
|
1411
|
+
const allowedBuilds = [];
|
|
1412
|
+
let execute = false;
|
|
1413
|
+
let json = false;
|
|
1414
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1415
|
+
const argument = args[index];
|
|
1416
|
+
if (argument === '--execute')
|
|
1417
|
+
execute = true;
|
|
1418
|
+
else if (argument === '--json')
|
|
1419
|
+
json = true;
|
|
1420
|
+
else if (argument === '--dsh-version' || argument === '--case-id' || argument === '--source-case-id'
|
|
1421
|
+
|| argument === '--source-fingerprint' || argument === '--contract-fingerprint' || argument === '--plane'
|
|
1422
|
+
|| argument === '--profile' || argument === '--runtime-id' || argument === '--artifact-sha256'
|
|
1423
|
+
|| argument === '--isolation-provider' || argument === '--timeout' || argument === '--report'
|
|
1424
|
+
|| argument === '--artifacts' || argument === '--driver-root' || argument === '--chromium-executable'
|
|
1425
|
+
|| argument === '--allow-build') {
|
|
1426
|
+
const value = args[index + 1];
|
|
1427
|
+
if (value === undefined || value.startsWith('-'))
|
|
1428
|
+
throw new Error(`${argument} requires a value`);
|
|
1429
|
+
if (argument === '--dsh-version')
|
|
1430
|
+
dshVersion = value;
|
|
1431
|
+
else if (argument === '--case-id')
|
|
1432
|
+
caseId = value;
|
|
1433
|
+
else if (argument === '--source-case-id')
|
|
1434
|
+
sourceCaseId = value;
|
|
1435
|
+
else if (argument === '--source-fingerprint')
|
|
1436
|
+
sourceFingerprint = value;
|
|
1437
|
+
else if (argument === '--contract-fingerprint')
|
|
1438
|
+
contractFingerprint = value;
|
|
1439
|
+
else if (argument === '--plane') {
|
|
1440
|
+
if (value !== 'web' && value !== 'tui')
|
|
1441
|
+
throw new Error('--plane must be web or tui');
|
|
1442
|
+
plane = value;
|
|
1443
|
+
}
|
|
1444
|
+
else if (argument === '--profile')
|
|
1445
|
+
profile = value;
|
|
1446
|
+
else if (argument === '--runtime-id')
|
|
1447
|
+
runtimeId = value;
|
|
1448
|
+
else if (argument === '--artifact-sha256')
|
|
1449
|
+
artifactSha256 = value;
|
|
1450
|
+
else if (argument === '--isolation-provider') {
|
|
1451
|
+
if (value !== 'github-actions-hosted-runner' && value !== 'firecracker' && value !== 'other') {
|
|
1452
|
+
throw new Error('--isolation-provider must be github-actions-hosted-runner, firecracker or other');
|
|
1453
|
+
}
|
|
1454
|
+
isolationProvider = value;
|
|
1455
|
+
}
|
|
1456
|
+
else if (argument === '--timeout') {
|
|
1457
|
+
const parsed = Number(value);
|
|
1458
|
+
if (!Number.isSafeInteger(parsed) || parsed < 30 || parsed > 600)
|
|
1459
|
+
throw new Error('--timeout must be an integer between 30 and 600 seconds');
|
|
1460
|
+
timeoutSeconds = parsed;
|
|
1461
|
+
}
|
|
1462
|
+
else if (argument === '--report')
|
|
1463
|
+
reportPath = value;
|
|
1464
|
+
else if (argument === '--artifacts')
|
|
1465
|
+
artifactsDirectory = value;
|
|
1466
|
+
else if (argument === '--driver-root')
|
|
1467
|
+
driverRoot = value;
|
|
1468
|
+
else if (argument === '--chromium-executable')
|
|
1469
|
+
chromiumExecutable = value;
|
|
1470
|
+
else
|
|
1471
|
+
allowedBuilds.push(value);
|
|
1472
|
+
index += 1;
|
|
1473
|
+
}
|
|
1474
|
+
else {
|
|
1475
|
+
throw new Error(`unknown option for probe dsh-surface: ${argument}`);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
const required = [
|
|
1479
|
+
['--dsh-version', dshVersion],
|
|
1480
|
+
['--case-id', caseId],
|
|
1481
|
+
['--source-case-id', sourceCaseId],
|
|
1482
|
+
['--source-fingerprint', sourceFingerprint],
|
|
1483
|
+
['--contract-fingerprint', contractFingerprint],
|
|
1484
|
+
['--plane', plane],
|
|
1485
|
+
['--profile', profile],
|
|
1486
|
+
['--runtime-id', runtimeId],
|
|
1487
|
+
['--artifact-sha256', artifactSha256],
|
|
1488
|
+
['--isolation-provider', isolationProvider],
|
|
1489
|
+
];
|
|
1490
|
+
const missing = required.find(([, value]) => value === undefined);
|
|
1491
|
+
if (missing !== undefined)
|
|
1492
|
+
throw new Error(`probe dsh-surface requires ${missing[0]}`);
|
|
1493
|
+
if (!execute)
|
|
1494
|
+
throw new Error('probe dsh-surface requires --execute because third-party code may run');
|
|
1495
|
+
if (process.env.UPSTREAM_RADAR_ISOLATED_RUNNER !== '1') {
|
|
1496
|
+
throw new Error('probe dsh-surface requires UPSTREAM_RADAR_ISOLATED_RUNNER=1 in the disposable environment');
|
|
1497
|
+
}
|
|
1498
|
+
const report = await observeDshPluginSurface({
|
|
1499
|
+
packageSpec,
|
|
1500
|
+
dshVersion: dshVersion,
|
|
1501
|
+
caseId: caseId,
|
|
1502
|
+
sourceCaseId: sourceCaseId,
|
|
1503
|
+
sourceFingerprint: sourceFingerprint,
|
|
1504
|
+
contractFingerprint: contractFingerprint,
|
|
1505
|
+
plane: plane,
|
|
1506
|
+
profile: profile,
|
|
1507
|
+
runtimeId: runtimeId,
|
|
1508
|
+
expectedArtifactSha256: artifactSha256,
|
|
1509
|
+
allowedBuilds,
|
|
1510
|
+
allowExecution: true,
|
|
1511
|
+
isolationProvider: isolationProvider,
|
|
1512
|
+
timeoutMs: timeoutSeconds * 1_000,
|
|
1513
|
+
...(artifactsDirectory === undefined ? {} : { artifactsDirectory }),
|
|
1514
|
+
...(driverRoot === undefined ? {} : { driverRoot }),
|
|
1515
|
+
...(chromiumExecutable === undefined ? {} : { chromiumExecutable }),
|
|
1516
|
+
});
|
|
1517
|
+
if (reportPath !== undefined) {
|
|
1518
|
+
const absoluteReportPath = resolve(reportPath);
|
|
1519
|
+
await mkdir(dirname(absoluteReportPath), { recursive: true });
|
|
1520
|
+
// Surface reports are an explicit hand-off from the unprivileged observer
|
|
1521
|
+
// container to the GitHub runner. They contain bounded compatibility
|
|
1522
|
+
// evidence and no inherited secrets, so the runner must be able to read
|
|
1523
|
+
// and upload them even when the container uses a different uid.
|
|
1524
|
+
await writeFile(absoluteReportPath, `${JSON.stringify(report, null, 2)}\n`, { mode: 0o644 });
|
|
1525
|
+
}
|
|
1526
|
+
process.stdout.write(json ? `${JSON.stringify(report, null, 2)}\n` : renderDshSurfaceObservation(report));
|
|
1527
|
+
return report.result === 'compatible' ? 0 : report.result === 'surface-incompatible' ? 2 : 1;
|
|
1528
|
+
}
|
|
1375
1529
|
async function runDshPluginReview(args) {
|
|
1376
1530
|
if (args[0] !== 'dsh-plugin')
|
|
1377
1531
|
throw new Error('review requires dsh-plugin');
|