upstream-radar 0.43.4 → 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.
Files changed (44) hide show
  1. package/README-zh-CN.md +92 -77
  2. package/README.md +125 -94
  3. package/dist/src/cli.js +177 -5
  4. package/dist/src/cli.js.map +1 -1
  5. package/dist/src/dsh-compatibility-ledger.d.ts +2 -0
  6. package/dist/src/dsh-compatibility-ledger.d.ts.map +1 -1
  7. package/dist/src/dsh-compatibility-ledger.js +5 -0
  8. package/dist/src/dsh-compatibility-ledger.js.map +1 -1
  9. package/dist/src/dsh-directory-feed.d.ts +7 -3
  10. package/dist/src/dsh-directory-feed.d.ts.map +1 -1
  11. package/dist/src/dsh-directory-feed.js +59 -25
  12. package/dist/src/dsh-directory-feed.js.map +1 -1
  13. package/dist/src/dsh-headless-agent-plan.d.ts +7 -0
  14. package/dist/src/dsh-headless-agent-plan.d.ts.map +1 -1
  15. package/dist/src/dsh-headless-agent-plan.js +20 -1
  16. package/dist/src/dsh-headless-agent-plan.js.map +1 -1
  17. package/dist/src/dsh-install-observation.d.ts +6 -0
  18. package/dist/src/dsh-install-observation.d.ts.map +1 -1
  19. package/dist/src/dsh-install-observation.js +2 -2
  20. package/dist/src/dsh-install-observation.js.map +1 -1
  21. package/dist/src/dsh-install-plan.d.ts +7 -0
  22. package/dist/src/dsh-install-plan.d.ts.map +1 -1
  23. package/dist/src/dsh-install-plan.js +8 -2
  24. package/dist/src/dsh-install-plan.js.map +1 -1
  25. package/dist/src/dsh-surface-observation.d.ts +160 -0
  26. package/dist/src/dsh-surface-observation.d.ts.map +1 -0
  27. package/dist/src/dsh-surface-observation.js +1031 -0
  28. package/dist/src/dsh-surface-observation.js.map +1 -0
  29. package/dist/src/dsh-surface.d.ts +127 -0
  30. package/dist/src/dsh-surface.d.ts.map +1 -0
  31. package/dist/src/dsh-surface.js +667 -0
  32. package/dist/src/dsh-surface.js.map +1 -0
  33. package/dist/src/index.d.ts +2 -0
  34. package/dist/src/index.d.ts.map +1 -1
  35. package/dist/src/index.js +2 -0
  36. package/dist/src/index.js.map +1 -1
  37. package/dist/src/upstream-observer.d.ts +6 -0
  38. package/dist/src/upstream-observer.d.ts.map +1 -1
  39. package/dist/src/upstream-observer.js +206 -127
  40. package/dist/src/upstream-observer.js.map +1 -1
  41. package/dist/src/version.d.ts +1 -1
  42. package/dist/src/version.js +1 -1
  43. package/docs/README.zh-CN.md +19 -19
  44. package/package.json +1 -1
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';
@@ -205,7 +206,8 @@ Usage:
205
206
  [--report <report.md>] [--dsh-version <v1>,<v2>,...]
206
207
  [--reverse-index <index.json>]
207
208
  [--dsh-agent-command <executable>]
208
- [--dsh-agent-arg <argument>] [--llm-env-file <path>] [--retry-pending]
209
+ [--dsh-agent-arg <argument>] [--llm-env-file <path>]
210
+ [--retry-pending | --retry-pending-only]
209
211
  [--ecosystem <dsh|codex|pi>] [--id <id>] [--package <name>]
210
212
  [--package-path <path>] [--lockfile <path>] [--lockfile-type <npm|pnpm>]
211
213
  [--ref <branch>] [--json]
@@ -238,6 +240,11 @@ keys; it never writes the key or endpoint to observations.json.
238
240
  If a ModelBest-style base URL ends in /llm/v1, a 404 also retries the known
239
241
  /llm/openai/v1 path.
240
242
 
243
+ --retry-pending-only consumes durable Agent tasks from observations.json
244
+ without reading GitHub/npm, reviewing artifacts, or changing source observation
245
+ points. It requires an Agent or model configuration and fails while any retry
246
+ still remains pending.
247
+
241
248
  The exact npm artifact review is independent of the DSH Agent/model. It runs
242
249
  when a meaningful change has a published package, and its dependency findings
243
250
  remain in the report even when no model is configured or the model endpoint is
@@ -275,6 +282,14 @@ Usage:
275
282
  upstream-radar probe dsh-install [npm:]<package>@<exact-version>
276
283
  --dsh-version <exact-version> --isolation-provider <provider> --execute
277
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]
278
293
 
279
294
  The load probes disable lifecycle scripts and check bundle registration/load.
280
295
  The dsh-install probe deliberately executes the exact plugin's lifecycle scripts
@@ -282,6 +297,10 @@ and loads the bundle while recording Linux process, network, and file-change
282
297
  evidence. Run it only inside a disposable, secret-free Linux environment. It
283
298
  requires both --execute and UPSTREAM_RADAR_ISOLATED_RUNNER=1.
284
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.
285
304
  `,
286
305
  review: `Upstream Radar — review one exact published DSH plugin in one command
287
306
 
@@ -471,13 +490,14 @@ Usage:
471
490
  upstream-radar doctor [config.json] [options]
472
491
  upstream-radar scan <directory-or-github-url> [--json] [--fail-on <warn|review|block|never>]
473
492
  upstream-radar inspect [npm:]<package>@<exact-version> [--deep] [--json] [--fail-on <warn|review|block|never>]
474
- upstream-radar observe <targets.yml|github-url> [--state <observations.json>] [--report <report.md>] [--dsh-version <v1>,<v2>,...] [--reverse-index <index.json>] [--dsh-agent-command <executable>] [--dsh-agent-arg <argument>] [--llm-env-file <path>] [--retry-pending] [--ecosystem <dsh|codex|pi>] [--id <id>] [--package <name>] [--package-path <path>] [--lockfile <path>] [--lockfile-type <npm|pnpm>] [--ref <branch>] [--json]
493
+ upstream-radar observe <targets.yml|github-url> [--state <observations.json>] [--report <report.md>] [--dsh-version <v1>,<v2>,...] [--reverse-index <index.json>] [--dsh-agent-command <executable>] [--dsh-agent-arg <argument>] [--llm-env-file <path>] [--retry-pending | --retry-pending-only] [--ecosystem <dsh|codex|pi>] [--id <id>] [--package <name>] [--package-path <path>] [--lockfile <path>] [--lockfile-type <npm|pnpm>] [--ref <branch>] [--json]
475
494
  upstream-radar graph <npm-lock|pnpm-lock> <lockfile> [--root <package>@<exact-version>] [--json]
476
495
  upstream-radar graph reverse <reports-directory> [--package <name>@<exact-version>] [--output <index.json>] [--json]
477
496
  upstream-radar profile-check [profile-directory] [--patch <path>] [--report <path>] [--summary] [--json]
478
497
  upstream-radar probe dsh-load <package.tgz> [--dsh-version <exact-version>] [--timeout <seconds>] [--keep-profile] [--json]
479
498
  upstream-radar probe dsh-matrix <package.tgz> --dsh-version <v1>[,<v2>,...] [--timeout <seconds>] [--keep-profile] [--json]
480
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]
481
501
  upstream-radar review dsh-plugin [npm:]<package>@<exact-version> --dsh-version <v1>,<v2>,... [--json]
482
502
  upstream-radar demo [--json]
483
503
  upstream-radar case dsh-web-ui [--json]
@@ -1214,8 +1234,10 @@ async function runProbe(args) {
1214
1234
  const mode = args[0];
1215
1235
  if (mode === 'dsh-install')
1216
1236
  return runDshInstallObservation(args.slice(1));
1237
+ if (mode === 'dsh-surface')
1238
+ return runDshSurfaceObservation(args.slice(1));
1217
1239
  if (mode !== 'dsh-load' && mode !== 'dsh-matrix')
1218
- throw new Error('probe requires dsh-load or dsh-matrix');
1240
+ throw new Error('probe requires dsh-load, dsh-matrix, dsh-install or dsh-surface');
1219
1241
  const packagePath = args[1];
1220
1242
  if (packagePath === undefined || packagePath.startsWith('-'))
1221
1243
  throw new Error(`probe ${mode} requires a package.tgz file`);
@@ -1366,6 +1388,144 @@ async function runDshInstallObservation(args) {
1366
1388
  process.stdout.write(json ? `${JSON.stringify(report, null, 2)}\n` : renderDshInstallObservation(report));
1367
1389
  return report.result === 'compatible' ? 0 : report.result === 'unknown' ? 1 : 2;
1368
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
+ }
1369
1529
  async function runDshPluginReview(args) {
1370
1530
  if (args[0] !== 'dsh-plugin')
1371
1531
  throw new Error('review requires dsh-plugin');
@@ -1852,6 +2012,7 @@ async function runObserve(args) {
1852
2012
  let reverseIndexPath;
1853
2013
  let registry;
1854
2014
  let retryPending = false;
2015
+ let retryPendingOnly = false;
1855
2016
  let json = false;
1856
2017
  let inlineEcosystem;
1857
2018
  let inlineId;
@@ -1870,6 +2031,9 @@ async function runObserve(args) {
1870
2031
  else if (argument === '--retry-pending') {
1871
2032
  retryPending = true;
1872
2033
  }
2034
+ else if (argument === '--retry-pending-only') {
2035
+ retryPendingOnly = true;
2036
+ }
1873
2037
  else if (argument === '--ecosystem' || argument === '--id' || argument === '--package' || argument === '--package-path' || argument === '--lockfile' || argument === '--lockfile-type' || argument === '--ref' || argument === '--dsh-version' || argument === '--state' || argument === '--report' || argument === '--reverse-index' || argument === '--dsh-agent-command' || argument === '--dsh-agent-arg' || argument === '--llm-env-file' || argument === '--registry') {
1874
2038
  const value = args[index + 1];
1875
2039
  if (value === undefined || (value.startsWith('-') && argument !== '--dsh-agent-arg'))
@@ -1959,10 +2123,17 @@ async function runObserve(args) {
1959
2123
  if (agentCommand !== undefined && llmEnvFile !== undefined) {
1960
2124
  throw new Error('observe accepts either --dsh-agent-command or --llm-env-file, not both');
1961
2125
  }
2126
+ if (retryPending && retryPendingOnly) {
2127
+ throw new Error('observe accepts either --retry-pending or --retry-pending-only, not both');
2128
+ }
2129
+ if (retryPendingOnly && agentCommand === undefined && llmEnvFile === undefined) {
2130
+ throw new Error('--retry-pending-only requires --dsh-agent-command or --llm-env-file');
2131
+ }
1962
2132
  const result = await runObserver(config, previousState, {
1963
2133
  source,
1964
2134
  ...(reverseDependencyIndex === undefined ? {} : { reverseDependencyIndex }),
1965
2135
  retryPending,
2136
+ retryPendingOnly,
1966
2137
  artifactReviewer: async (spec, target) => reviewObserverArtifact(spec, target, registry),
1967
2138
  ...(agentOptions === undefined ? {} : {
1968
2139
  agent: (task, prompt) => runDshAgentCommand(task, prompt, agentOptions),
@@ -1978,10 +2149,11 @@ async function runObserve(args) {
1978
2149
  }
1979
2150
  process.stdout.write(json ? reportJson : renderObserverReport(result.report));
1980
2151
  const observerExit = observerExitCode(result.report);
2152
+ const pendingRetryExit = retryPendingOnly && result.report.pendingTasks.length > 0 ? 1 : 0;
1981
2153
  if (result.report.agent.failed > 0) {
1982
- process.stderr.write('upstream-radar: static observation completed; DSH Agent/model analysis remains pending. See the report and rerun with --retry-pending.\n');
2154
+ process.stderr.write(`upstream-radar: DSH Agent/model analysis remains pending. See the report and rerun with ${retryPendingOnly ? '--retry-pending-only' : '--retry-pending'}.\n`);
1983
2155
  }
1984
- return observerExit;
2156
+ return observerExit === 0 && pendingRetryExit === 0 ? 0 : 1;
1985
2157
  }
1986
2158
  async function runQuickstart(args) {
1987
2159
  let directory = process.cwd();