upstream-radar 0.40.0 → 0.41.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 (48) hide show
  1. package/README.md +123 -225
  2. package/dist/src/cli.js +11 -2
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/dsh-compatibility-ir.d.ts +98 -0
  5. package/dist/src/dsh-compatibility-ir.d.ts.map +1 -0
  6. package/dist/src/dsh-compatibility-ir.js +389 -0
  7. package/dist/src/dsh-compatibility-ir.js.map +1 -0
  8. package/dist/src/dsh-compatibility-issues.d.ts +44 -0
  9. package/dist/src/dsh-compatibility-issues.d.ts.map +1 -0
  10. package/dist/src/dsh-compatibility-issues.js +185 -0
  11. package/dist/src/dsh-compatibility-issues.js.map +1 -0
  12. package/dist/src/dsh-compatibility-ledger.d.ts +150 -0
  13. package/dist/src/dsh-compatibility-ledger.d.ts.map +1 -0
  14. package/dist/src/dsh-compatibility-ledger.js +645 -0
  15. package/dist/src/dsh-compatibility-ledger.js.map +1 -0
  16. package/dist/src/dsh-install-observation.d.ts +88 -1
  17. package/dist/src/dsh-install-observation.d.ts.map +1 -1
  18. package/dist/src/dsh-install-observation.js +579 -6
  19. package/dist/src/dsh-install-observation.js.map +1 -1
  20. package/dist/src/dsh-install-plan.d.ts +16 -6
  21. package/dist/src/dsh-install-plan.d.ts.map +1 -1
  22. package/dist/src/dsh-install-plan.js +272 -46
  23. package/dist/src/dsh-install-plan.js.map +1 -1
  24. package/dist/src/dsh-plugin.d.ts.map +1 -1
  25. package/dist/src/dsh-plugin.js +6 -2
  26. package/dist/src/dsh-plugin.js.map +1 -1
  27. package/dist/src/dsh-runtime.d.ts +6 -0
  28. package/dist/src/dsh-runtime.d.ts.map +1 -1
  29. package/dist/src/dsh-runtime.js +38 -0
  30. package/dist/src/dsh-runtime.js.map +1 -1
  31. package/dist/src/index.d.ts +7 -4
  32. package/dist/src/index.d.ts.map +1 -1
  33. package/dist/src/index.js +4 -1
  34. package/dist/src/index.js.map +1 -1
  35. package/dist/src/installed-graph.d.ts.map +1 -1
  36. package/dist/src/installed-graph.js +105 -26
  37. package/dist/src/installed-graph.js.map +1 -1
  38. package/dist/src/radar-types.d.ts +15 -0
  39. package/dist/src/radar-types.d.ts.map +1 -1
  40. package/dist/src/semver.d.ts.map +1 -1
  41. package/dist/src/semver.js +7 -1
  42. package/dist/src/semver.js.map +1 -1
  43. package/dist/src/version.d.ts +1 -1
  44. package/dist/src/version.js +1 -1
  45. package/docs/README.zh-CN.md +25 -23
  46. package/package.json +1 -1
  47. package/schemas/dsh-compatibility-ir.schema.json +89 -0
  48. package/schemas/dsh-install-observation.schema.json +135 -2
@@ -1,9 +1,13 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { spawn } from 'node:child_process';
3
3
  import { constants } from 'node:fs';
4
- import { lstat, mkdir, mkdtemp, open, readdir, rm, writeFile } from 'node:fs/promises';
4
+ import { lstat, mkdir, mkdtemp, open, readdir, realpath, rm, writeFile } from 'node:fs/promises';
5
5
  import { tmpdir } from 'node:os';
6
- import { basename, join, relative, resolve, sep } from 'node:path';
6
+ import { basename, dirname, join, relative, resolve, sep } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { parsePnpmLockGraph } from './graph.js';
9
+ import { discoverDshRuntimeHostNodeModulesDirectory, discoverDshRuntimePackage, discoverDshRuntimePackageDirectory, } from './dsh-runtime.js';
10
+ import { parseInstalledNodeModulesGraph } from './installed-graph.js';
7
11
  import { parseNpmSpec } from './npm.js';
8
12
  import { satisfiesSemverRange } from './semver.js';
9
13
  import { parseNpmTarball } from './tar.js';
@@ -20,8 +24,19 @@ const MAX_TRACE_EVENTS = 512;
20
24
  const MAX_SNAPSHOT_ENTRIES = 25_000;
21
25
  const MAX_DIFF_PATHS = 512;
22
26
  const MAX_ALLOWED_BUILDS = 16;
27
+ const MAX_PROFILE_LOCKFILE_BYTES = 16 * 1024 * 1024;
28
+ const MAX_PROFILE_GRAPH_GAPS = 32;
29
+ const MAX_PLUGIN_PEERS = 64;
30
+ const MAX_STATIC_PEER_SCAN_BYTES = 8 * 1024 * 1024;
31
+ const MAX_RUNTIME_DISCOVERY_DIRECTORIES = 12_000;
32
+ const MAX_RUNTIME_DISCOVERY_DEPTH = 16;
23
33
  const PROFILE = 'headless';
24
34
  const LIFECYCLE_SCRIPTS = ['preinstall', 'install', 'postinstall', 'prepare'];
35
+ const PROFILE_LOCKFILE_CANDIDATES = [
36
+ ['pnpm-lock.yaml'],
37
+ ['node_modules', '.pnpm', 'lock.yaml'],
38
+ ];
39
+ const SYNTHETIC_PROFILE_GRAPH_ROOT = { name: 'dsh-profile-headless', version: '0.0.0' };
25
40
  function normalizeAllowedBuilds(values) {
26
41
  if (values === undefined)
27
42
  return [];
@@ -37,6 +52,82 @@ function normalizeAllowedBuilds(values) {
37
52
  }
38
53
  return [...names].sort();
39
54
  }
55
+ function isNpmPackageName(value) {
56
+ return /^(?:@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*|[a-z0-9][a-z0-9._-]*)$/.test(value);
57
+ }
58
+ function staticPeerUsage(entries, requirements) {
59
+ const state = new Map(requirements.map(requirement => [requirement.name, 'no-literal-reference-observed']));
60
+ let scanned = 0;
61
+ let incomplete = false;
62
+ for (const entry of entries) {
63
+ if (entry.type !== 'file' || entry.contents === undefined
64
+ || !/\.(?:[cm]?[jt]sx?|vue|svelte)$/i.test(entry.path))
65
+ continue;
66
+ if (scanned + entry.contents.length > MAX_STATIC_PEER_SCAN_BYTES) {
67
+ incomplete = true;
68
+ continue;
69
+ }
70
+ scanned += entry.contents.length;
71
+ const text = entry.contents.toString('utf8');
72
+ const declarationFile = /\.d\.(?:[cm]?ts)$/i.test(entry.path);
73
+ for (const requirement of requirements) {
74
+ if (state.get(requirement.name) === 'runtime-import-observed')
75
+ continue;
76
+ const escaped = requirement.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
77
+ const literal = `['"]${escaped}(?:/[^'"]*)?['"]`;
78
+ // Stay within one statement line. A wider expression can start at an
79
+ // unrelated runtime import and accidentally consume a later `import
80
+ // type`, which would turn declaration-only evidence into a false runtime
81
+ // claim. Missing a heavily formatted import is safer than that claim.
82
+ const typeOnly = new RegExp(`(?:^|[;\\n])[\\t ]*import[\\t ]+type\\b[^;\\n]{0,1024}?\\bfrom[\\t ]*${literal}`);
83
+ const runtime = new RegExp([
84
+ `(?:^|[;\\n])[\\t ]*import[\\t ]+(?!type\\b)(?:[^;\\n]{0,1024}?[\\t ]+from[\\t ]+)?${literal}`,
85
+ `(?:^|[;\\n])[\\t ]*export[\\t ]+(?!type\\b)[^;\\n]{0,1024}?[\\t ]+from[\\t ]+${literal}`,
86
+ `\\b(?:require|import)\\s*\\(\\s*${literal}`,
87
+ ].join('|'));
88
+ const literalReference = new RegExp(literal);
89
+ if (!declarationFile && runtime.test(text)) {
90
+ state.set(requirement.name, 'runtime-import-observed');
91
+ }
92
+ else if (declarationFile ? literalReference.test(text) : typeOnly.test(text)) {
93
+ state.set(requirement.name, 'type-only-reference-observed');
94
+ }
95
+ }
96
+ }
97
+ return requirements.map(requirement => {
98
+ const observed = state.get(requirement.name) ?? 'no-literal-reference-observed';
99
+ return observed === 'no-literal-reference-observed' && incomplete ? 'scan-incomplete' : observed;
100
+ });
101
+ }
102
+ function requiredPeerDependencies(manifest, entries) {
103
+ const peers = typeof manifest.peerDependencies === 'object' && manifest.peerDependencies !== null && !Array.isArray(manifest.peerDependencies)
104
+ ? manifest.peerDependencies
105
+ : {};
106
+ const metadata = typeof manifest.peerDependenciesMeta === 'object' && manifest.peerDependenciesMeta !== null && !Array.isArray(manifest.peerDependenciesMeta)
107
+ ? manifest.peerDependenciesMeta
108
+ : {};
109
+ const requirements = [];
110
+ for (const [name, rawRange] of Object.entries(peers).sort(([left], [right]) => left.localeCompare(right))) {
111
+ if (!isNpmPackageName(name))
112
+ throw new Error(`packed artifact has an invalid peer dependency name: ${name}`);
113
+ if (typeof rawRange !== 'string' || rawRange.trim() === '' || rawRange.length > 512) {
114
+ throw new Error(`packed artifact has an invalid peer dependency range for ${name}`);
115
+ }
116
+ const peerMetadata = metadata[name];
117
+ const optional = typeof peerMetadata === 'object' && peerMetadata !== null && !Array.isArray(peerMetadata)
118
+ && peerMetadata.optional === true;
119
+ if (!optional)
120
+ requirements.push({ name, required: bounded(rawRange.trim(), 512) });
121
+ }
122
+ if (requirements.length > MAX_PLUGIN_PEERS) {
123
+ throw new Error(`packed artifact declares more than ${MAX_PLUGIN_PEERS} required peer dependencies`);
124
+ }
125
+ const usages = staticPeerUsage(entries, requirements);
126
+ return requirements.map((requirement, index) => ({
127
+ ...requirement,
128
+ staticUsage: usages[index] ?? 'scan-incomplete',
129
+ }));
130
+ }
40
131
  function bounded(value, maximum = MAX_REPORT_DETAIL_BYTES) {
41
132
  const normalized = value.replace(/[\u0000-\u001f\u007f-\u009f]/g, character => (`\\u${character.codePointAt(0)?.toString(16).padStart(4, '0') ?? '0000'}`));
42
133
  return normalized.length <= maximum ? normalized : `${normalized.slice(0, maximum - 1)}…`;
@@ -456,6 +547,7 @@ async function parsePackedArtifact(result, artifactDirectory, expectedName, expe
456
547
  }
457
548
  const nodeEngine = rawNodeEngine === undefined || rawNodeEngine === '' ? undefined : bounded(rawNodeEngine, 512);
458
549
  const integrity = typeof item.integrity === 'string' ? bounded(item.integrity, 1_024) : undefined;
550
+ const peerRequirements = requiredPeerDependencies(manifest, parsed.entries);
459
551
  return {
460
552
  path,
461
553
  filename,
@@ -465,6 +557,7 @@ async function parsePackedArtifact(result, artifactDirectory, expectedName, expe
465
557
  bundlePatch,
466
558
  ...(nodeEngine === undefined ? {} : { nodeEngine }),
467
559
  lifecycleScripts,
560
+ requiredPeerDependencies: peerRequirements,
468
561
  };
469
562
  }
470
563
  async function snapshotTree(root) {
@@ -602,16 +695,473 @@ async function registeredBundle(dshHome, packageName) {
602
695
  return false;
603
696
  }
604
697
  }
698
+ function isNotFound(error) {
699
+ return typeof error === 'object' && error !== null
700
+ && 'code' in error
701
+ && error.code === 'ENOENT';
702
+ }
703
+ /**
704
+ * DSH versions have emitted both a project lockfile and pnpm's virtual-store
705
+ * lockfile. Read only those fixed descendants, refuse every symlink in their
706
+ * path, and never retain the lockfile contents outside the disposable runner.
707
+ */
708
+ async function readProfileLockfile(dshHome) {
709
+ const profileDirectory = join(dshHome, 'profiles', PROFILE);
710
+ try {
711
+ const profileMetadata = await lstat(profileDirectory);
712
+ if (!profileMetadata.isDirectory() || profileMetadata.isSymbolicLink())
713
+ return undefined;
714
+ }
715
+ catch {
716
+ return undefined;
717
+ }
718
+ for (const segments of PROFILE_LOCKFILE_CANDIDATES) {
719
+ let current = profileDirectory;
720
+ try {
721
+ for (let index = 0; index < segments.length; index += 1) {
722
+ const segment = segments[index];
723
+ current = join(current, segment);
724
+ const metadata = await lstat(current);
725
+ if (metadata.isSymbolicLink())
726
+ return undefined;
727
+ if (index < segments.length - 1 && !metadata.isDirectory())
728
+ return undefined;
729
+ }
730
+ return await readRegularFileNoFollow(current, MAX_PROFILE_LOCKFILE_BYTES);
731
+ }
732
+ catch (error) {
733
+ if (isNotFound(error))
734
+ continue;
735
+ return undefined;
736
+ }
737
+ }
738
+ return undefined;
739
+ }
740
+ function profileGraphRoot(manifest) {
741
+ return typeof manifest.name === 'string' && manifest.name.length > 0
742
+ && typeof manifest.version === 'string' && EXACT_VERSION.test(manifest.version)
743
+ ? { name: manifest.name, version: manifest.version }
744
+ : SYNTHETIC_PROFILE_GRAPH_ROOT;
745
+ }
746
+ function graphGaps(graph) {
747
+ if (graph.unresolved === undefined || graph.unresolved.length === 0)
748
+ return undefined;
749
+ return graph.unresolved.slice(0, MAX_PROFILE_GRAPH_GAPS).map(gap => ({
750
+ from: bounded(gap.from, 512),
751
+ name: bounded(gap.name, 214),
752
+ spec: bounded(gap.spec, 512),
753
+ kind: gap.kind,
754
+ }));
755
+ }
756
+ function pluginPeerContractEvidence(contracts) {
757
+ const entries = [...(contracts ?? [])].sort((left, right) => left.name.localeCompare(right.name));
758
+ const issues = entries
759
+ .filter((entry) => entry.status !== 'satisfied')
760
+ .sort((left, right) => left.name.localeCompare(right.name));
761
+ return {
762
+ declared: entries.length,
763
+ satisfied: entries.filter(entry => entry.status === 'satisfied').length,
764
+ mismatched: entries.filter(entry => entry.status === 'mismatched').length,
765
+ indeterminate: entries.filter(entry => entry.status === 'indeterminate').length,
766
+ missing: entries.filter(entry => entry.status === 'missing').length,
767
+ relations: entries.map(entry => ({
768
+ name: bounded(entry.name, 214),
769
+ required: bounded(entry.required, 512),
770
+ status: entry.status,
771
+ staticUsage: entry.staticUsage,
772
+ ...(entry.resolvedVersion === undefined ? {} : { resolvedVersion: bounded(entry.resolvedVersion, 256) }),
773
+ })),
774
+ ...(issues.length === 0 ? {} : {
775
+ issues: issues.slice(0, MAX_PROFILE_GRAPH_GAPS).map(issue => ({
776
+ name: bounded(issue.name, 214),
777
+ required: bounded(issue.required, 512),
778
+ status: issue.status,
779
+ staticUsage: issue.staticUsage,
780
+ ...(issue.resolvedVersion === undefined ? {} : { resolvedVersion: bounded(issue.resolvedVersion, 256) }),
781
+ })),
782
+ }),
783
+ };
784
+ }
785
+ function graphGapPartitions(graph) {
786
+ const required = [];
787
+ const optional = [];
788
+ for (const gap of graph.unresolved ?? []) {
789
+ if (gap.kind === 'optional')
790
+ optional.push(gap);
791
+ else
792
+ required.push(gap);
793
+ }
794
+ return { required, optional };
795
+ }
796
+ function isLexicallyInside(root, candidate) {
797
+ const child = relative(resolve(root), resolve(candidate));
798
+ return child === '' || (!child.startsWith(`..${sep}`) && child !== '..');
799
+ }
800
+ const PROFILE_PEER_RESOLUTION_SCHEMA = 'upstream-radar.profile-peer-resolution/v1alpha1';
801
+ function indeterminatePeerContracts(requirements) {
802
+ return requirements.map(requirement => ({ ...requirement, status: 'indeterminate' }));
803
+ }
804
+ async function peerVersionFromResolvedModule(url, expectedName, sandboxRoot) {
805
+ let resolvedModule;
806
+ try {
807
+ const parsed = new URL(url);
808
+ if (parsed.protocol !== 'file:')
809
+ return undefined;
810
+ resolvedModule = await realpath(fileURLToPath(parsed));
811
+ }
812
+ catch {
813
+ return undefined;
814
+ }
815
+ let sandboxReal;
816
+ try {
817
+ sandboxReal = await realpath(sandboxRoot);
818
+ }
819
+ catch {
820
+ return undefined;
821
+ }
822
+ if (!isLexicallyInside(sandboxReal, resolvedModule))
823
+ return undefined;
824
+ let cursor = dirname(resolvedModule);
825
+ for (let depth = 0; depth < 32 && isLexicallyInside(sandboxReal, cursor); depth += 1) {
826
+ try {
827
+ const manifest = JSON.parse((await readRegularFileNoFollow(join(cursor, 'package.json'), 1 * 1024 * 1024)).toString('utf8'));
828
+ if (typeof manifest === 'object' && manifest !== null && !Array.isArray(manifest)) {
829
+ const item = manifest;
830
+ if (item.name === expectedName && typeof item.version === 'string' && EXACT_VERSION.test(item.version))
831
+ return item.version;
832
+ }
833
+ }
834
+ catch {
835
+ // Continue toward the package root; a module may live in a nested directory.
836
+ }
837
+ const parent = dirname(cursor);
838
+ if (parent === cursor)
839
+ break;
840
+ cursor = parent;
841
+ }
842
+ return undefined;
843
+ }
844
+ async function readProfilePeerContracts(path, requirements, sandboxRoot) {
845
+ if (requirements.length === 0)
846
+ return [];
847
+ let records;
848
+ try {
849
+ const raw = JSON.parse((await readRegularFileNoFollow(path, 64 * 1024)).toString('utf8'));
850
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
851
+ return indeterminatePeerContracts(requirements);
852
+ const item = raw;
853
+ if (item.schema !== PROFILE_PEER_RESOLUTION_SCHEMA || !Array.isArray(item.peers) || item.peers.length !== requirements.length) {
854
+ return indeterminatePeerContracts(requirements);
855
+ }
856
+ records = item.peers.map((value) => {
857
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
858
+ throw new Error('invalid peer resolver record');
859
+ const peer = value;
860
+ if (typeof peer.name !== 'string' || (peer.status !== 'resolved' && peer.status !== 'missing')) {
861
+ throw new Error('invalid peer resolver record');
862
+ }
863
+ if (peer.status === 'resolved' && (typeof peer.url !== 'string' || peer.url.length === 0 || peer.url.length > 4_096)) {
864
+ throw new Error('resolved peer has no bounded URL');
865
+ }
866
+ return {
867
+ name: peer.name,
868
+ status: peer.status,
869
+ ...(typeof peer.url === 'string' ? { url: peer.url } : {}),
870
+ };
871
+ });
872
+ }
873
+ catch {
874
+ return indeterminatePeerContracts(requirements);
875
+ }
876
+ const byName = new Map(records.map(record => [record.name, record]));
877
+ if (byName.size !== requirements.length || requirements.some(requirement => !byName.has(requirement.name))) {
878
+ return indeterminatePeerContracts(requirements);
879
+ }
880
+ const contracts = [];
881
+ for (const requirement of requirements) {
882
+ const record = byName.get(requirement.name);
883
+ if (record === undefined || record.status === 'missing') {
884
+ contracts.push({ ...requirement, status: 'missing' });
885
+ continue;
886
+ }
887
+ const resolvedVersion = record.url === undefined
888
+ ? undefined
889
+ : await peerVersionFromResolvedModule(record.url, requirement.name, sandboxRoot);
890
+ if (resolvedVersion === undefined) {
891
+ contracts.push({ ...requirement, status: 'indeterminate' });
892
+ continue;
893
+ }
894
+ const evaluation = satisfiesSemverRange(resolvedVersion, requirement.required);
895
+ contracts.push({
896
+ ...requirement,
897
+ status: evaluation === true ? 'satisfied' : evaluation === false ? 'mismatched' : 'indeterminate',
898
+ resolvedVersion,
899
+ });
900
+ }
901
+ return contracts;
902
+ }
903
+ /**
904
+ * Run the plugin's real ESM entry from the profile resolution anchor, then
905
+ * boot DSH. The config dumper intentionally skips `!!js` and module imports;
906
+ * this tiny trusted wrapper proves that the installed plugin can resolve its
907
+ * own direct imports before the DSH headless app exits through `--help`.
908
+ */
909
+ async function writeProfileLoadProbe(dshHome, pluginName, dshVersion, pnpmCommand, peerRequirements) {
910
+ const profileDirectory = join(dshHome, 'profiles', PROFILE);
911
+ const profileMetadata = await lstat(profileDirectory);
912
+ if (!profileMetadata.isDirectory() || profileMetadata.isSymbolicLink()) {
913
+ throw new Error('the DSH profile directory is not a regular directory for the load probe');
914
+ }
915
+ const [homeReal, profileReal] = await Promise.all([realpath(dshHome), realpath(profileDirectory)]);
916
+ if (!isLexicallyInside(homeReal, profileReal)) {
917
+ throw new Error('the DSH profile directory escaped the controlled DSH home');
918
+ }
919
+ const probePath = join(profileDirectory, '.upstream-radar-load-probe.mjs');
920
+ const peerResolutionPath = join(profileDirectory, '.upstream-radar-peer-resolution.json');
921
+ const dshBootArgs = dshArgs(dshVersion, ['--profile', PROFILE, '--help']);
922
+ const contents = [
923
+ "import { spawn } from 'node:child_process'",
924
+ "import { writeFile } from 'node:fs/promises'",
925
+ `const peerRequirements = ${JSON.stringify(peerRequirements)}`,
926
+ 'const peers = []',
927
+ 'for (const peer of peerRequirements) {',
928
+ ' try { peers.push({ name: peer.name, status: \'resolved\', url: import.meta.resolve(peer.name) }) }',
929
+ ' catch { peers.push({ name: peer.name, status: \'missing\' }) }',
930
+ '}',
931
+ `await writeFile(${JSON.stringify(peerResolutionPath)}, JSON.stringify({ schema: ${JSON.stringify(PROFILE_PEER_RESOLUTION_SCHEMA)}, peers }), { encoding: 'utf8', mode: 0o600, flag: 'wx' })`,
932
+ `await import(${JSON.stringify(pluginName)})`,
933
+ `const child = spawn(${JSON.stringify(pnpmCommand)}, ${JSON.stringify(dshBootArgs)}, { cwd: process.cwd(), env: process.env, stdio: 'inherit' })`,
934
+ "const exitCode = await new Promise(resolve => {",
935
+ " child.once('error', error => { console.error(error.message); resolve(1) })",
936
+ " child.once('close', code => resolve(code ?? 1))",
937
+ '})',
938
+ 'process.exitCode = exitCode',
939
+ '',
940
+ ].join('\n');
941
+ // Never overwrite a path a target package may have planted in the profile.
942
+ await writeFile(probePath, contents, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
943
+ return { path: probePath, peerResolutionPath, profileDirectory };
944
+ }
945
+ /**
946
+ * `pnpm dlx` owns the DSH host dependency plane, rather than a plugin-created
947
+ * symlink in the profile. Discover the exact cached DSH package by manifest,
948
+ * with bounded directory traversal and no symlink following.
949
+ */
950
+ async function discoverExactDshRuntime(cacheHome, dshVersion) {
951
+ const root = resolve(cacheHome, 'pnpm', 'dlx');
952
+ const rootReal = await realpath(root);
953
+ const queue = [{ path: root, depth: 0 }];
954
+ const candidates = new Set();
955
+ let visited = 0;
956
+ while (queue.length > 0) {
957
+ const current = queue.shift();
958
+ if (current === undefined)
959
+ break;
960
+ if (visited >= MAX_RUNTIME_DISCOVERY_DIRECTORIES) {
961
+ throw new Error(`DSH runtime discovery exceeds ${MAX_RUNTIME_DISCOVERY_DIRECTORIES} directories`);
962
+ }
963
+ visited += 1;
964
+ let entries;
965
+ try {
966
+ entries = await readdir(current.path, { withFileTypes: true });
967
+ }
968
+ catch (error) {
969
+ if (isNotFound(error))
970
+ continue;
971
+ throw error;
972
+ }
973
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
974
+ const child = join(current.path, entry.name);
975
+ if (entry.isDirectory()) {
976
+ if (current.depth < MAX_RUNTIME_DISCOVERY_DEPTH)
977
+ queue.push({ path: child, depth: current.depth + 1 });
978
+ continue;
979
+ }
980
+ if (!entry.isFile() || entry.name !== 'package.json')
981
+ continue;
982
+ let manifest;
983
+ try {
984
+ manifest = JSON.parse((await readRegularFileNoFollow(child, 1 * 1024 * 1024)).toString('utf8'));
985
+ }
986
+ catch {
987
+ continue;
988
+ }
989
+ if (typeof manifest !== 'object' || manifest === null || Array.isArray(manifest))
990
+ continue;
991
+ const item = manifest;
992
+ if (item.name === '@deepseek-ai/dsh' && item.version === dshVersion)
993
+ candidates.add(child);
994
+ }
995
+ }
996
+ if (candidates.size === 0)
997
+ throw new Error(`exact @deepseek-ai/dsh@${dshVersion} was not found in the controlled pnpm dlx cache`);
998
+ if (candidates.size > 1)
999
+ throw new Error(`multiple exact @deepseek-ai/dsh@${dshVersion} packages were found in the controlled pnpm dlx cache`);
1000
+ const manifestPath = [...candidates][0];
1001
+ const packageDirectory = discoverDshRuntimePackageDirectory(manifestPath);
1002
+ const packageCoordinate = discoverDshRuntimePackage(manifestPath);
1003
+ const nodeModulesDirectory = discoverDshRuntimeHostNodeModulesDirectory(manifestPath);
1004
+ if (packageDirectory === undefined || packageCoordinate === undefined || nodeModulesDirectory === undefined) {
1005
+ throw new Error('the exact DSH package did not expose a usable dependency plane');
1006
+ }
1007
+ if (!isLexicallyInside(rootReal, packageDirectory) || !isLexicallyInside(rootReal, nodeModulesDirectory)) {
1008
+ throw new Error('the exact DSH dependency plane escaped the controlled pnpm dlx cache');
1009
+ }
1010
+ if (packageCoordinate.name !== '@deepseek-ai/dsh' || packageCoordinate.version !== dshVersion) {
1011
+ throw new Error('the discovered DSH package does not match the requested exact version');
1012
+ }
1013
+ return {
1014
+ packageDirectory,
1015
+ nodeModulesDirectory,
1016
+ package: { ecosystem: 'npm', name: '@deepseek-ai/dsh', version: packageCoordinate.version },
1017
+ };
1018
+ }
1019
+ async function profileResolutionEvidence(dshHome) {
1020
+ const profileDirectory = join(dshHome, 'profiles', PROFILE);
1021
+ const lockfile = await readProfileLockfile(dshHome);
1022
+ if (lockfile === undefined)
1023
+ return {};
1024
+ const profileLockfile = {
1025
+ sha256: createHash('sha256').update(lockfile).digest('hex'),
1026
+ bytes: lockfile.length,
1027
+ };
1028
+ try {
1029
+ const manifestBuffer = await readRegularFileNoFollow(join(profileDirectory, 'package.json'), 4 * 1024 * 1024);
1030
+ const manifest = JSON.parse(manifestBuffer.toString('utf8'));
1031
+ const graph = parsePnpmLockGraph(lockfile.toString('utf8'), profileGraphRoot(manifest));
1032
+ const gaps = graphGaps(graph);
1033
+ return {
1034
+ profileLockfile: {
1035
+ ...profileLockfile,
1036
+ ...(graph.digest === undefined ? {} : { graphDigest: graph.digest }),
1037
+ nodes: graph.nodes.length,
1038
+ edges: graph.edges.length,
1039
+ unresolved: graph.unresolved?.length ?? 0,
1040
+ ...(gaps === undefined ? {} : { unresolvedDependencies: gaps }),
1041
+ },
1042
+ };
1043
+ }
1044
+ catch {
1045
+ return { profileLockfile };
1046
+ }
1047
+ }
1048
+ async function runtimeGraphEvidence(dshHome, rootPackage, dshVersion, cacheHome, profileResolvedPeerContracts) {
1049
+ try {
1050
+ const dshRuntime = await discoverExactDshRuntime(cacheHome, dshVersion);
1051
+ const graph = await parseInstalledNodeModulesGraph(join(dshHome, 'profiles', PROFILE), rootPackage, {
1052
+ hostNodeModulesDirectory: dshRuntime.nodeModulesDirectory,
1053
+ hostRuntimeSource: 'dsh-process',
1054
+ hostRuntimePackage: dshRuntime.package,
1055
+ hostRuntimePackageDirectory: dshRuntime.packageDirectory,
1056
+ });
1057
+ if (graph.digest === undefined)
1058
+ return {};
1059
+ const gaps = graphGapPartitions(graph);
1060
+ const contracts = profileResolvedPeerContracts ?? graph.rootPeerContracts?.map(contract => ({
1061
+ ...contract,
1062
+ staticUsage: 'scan-incomplete',
1063
+ }));
1064
+ const concretelyResolvedRootPeers = new Set((contracts ?? [])
1065
+ .filter(contract => contract.status === 'satisfied' || contract.status === 'mismatched')
1066
+ .map(contract => contract.name));
1067
+ const effectiveRequiredGaps = gaps.required.filter(gap => !(gap.from === graph.rootNodeId
1068
+ && gap.kind === 'peer'
1069
+ && concretelyResolvedRootPeers.has(gap.name)));
1070
+ const requiredGaps = graphGaps({ unresolved: effectiveRequiredGaps });
1071
+ const optionalGaps = graphGaps({ unresolved: gaps.optional });
1072
+ return {
1073
+ runtimeGraph: {
1074
+ digest: graph.digest,
1075
+ nodes: graph.nodes.length,
1076
+ edges: graph.edges.length,
1077
+ unresolved: effectiveRequiredGaps.length,
1078
+ ...(requiredGaps === undefined ? {} : { unresolvedDependencies: requiredGaps }),
1079
+ ...(gaps.optional.length === 0 ? {} : {
1080
+ optionalUnavailable: gaps.optional.length,
1081
+ ...(optionalGaps === undefined ? {} : { optionalUnavailableDependencies: optionalGaps }),
1082
+ }),
1083
+ pluginPeerContracts: pluginPeerContractEvidence(contracts),
1084
+ ...(graph.hostRuntime === undefined ? {} : {
1085
+ hostRuntime: {
1086
+ source: graph.hostRuntime.source,
1087
+ resolvedNodes: graph.hostRuntime.resolvedNodes,
1088
+ ...(graph.hostRuntime.package === undefined ? {} : { dshVersion: graph.hostRuntime.package.version }),
1089
+ },
1090
+ }),
1091
+ },
1092
+ };
1093
+ }
1094
+ catch (error) {
1095
+ return {
1096
+ runtimeGraphError: bounded(error instanceof Error ? error.message : String(error), 512),
1097
+ };
1098
+ }
1099
+ }
1100
+ async function resolutionEvidence(dshHome, rootPackage, dshVersion, cacheHome, profileResolvedPeerContracts) {
1101
+ const [profile, runtime] = await Promise.all([
1102
+ profileResolutionEvidence(dshHome),
1103
+ runtimeGraphEvidence(dshHome, rootPackage, dshVersion, cacheHome, profileResolvedPeerContracts),
1104
+ ]);
1105
+ return { ...profile, ...runtime };
1106
+ }
605
1107
  function finishReport(report, result, reason) {
606
1108
  report.completedAt = new Date().toISOString();
607
1109
  report.result = result;
608
1110
  report.reason = reason;
609
1111
  return report;
610
1112
  }
1113
+ /**
1114
+ * A load success is necessary but not enough for the compatibility claim. The
1115
+ * final verdict also requires a complete required-edge graph and a direct
1116
+ * plugin peer contract that the observer could actually evaluate.
1117
+ */
1118
+ function finalCompatibilityConclusion(resolution) {
1119
+ const graph = resolution.runtimeGraph;
1120
+ if (graph === undefined) {
1121
+ return {
1122
+ result: 'unknown',
1123
+ reason: resolution.runtimeGraphError === undefined
1124
+ ? 'the exact artifact installed and loaded, but the effective DSH runtime graph was not established'
1125
+ : `the exact artifact installed and loaded, but the effective DSH runtime graph could not be established: ${resolution.runtimeGraphError}`,
1126
+ };
1127
+ }
1128
+ const contracts = graph.pluginPeerContracts;
1129
+ const firstIssue = contracts.issues?.[0];
1130
+ if (contracts.mismatched > 0 || contracts.missing > 0) {
1131
+ const detail = firstIssue === undefined
1132
+ ? `${contracts.mismatched + contracts.missing} required plugin peer contract(s) do not match the DSH runtime`
1133
+ : firstIssue.status === 'missing'
1134
+ ? `${firstIssue.name}@${firstIssue.required} was not resolved by the DSH runtime (${firstIssue.staticUsage})`
1135
+ : `${firstIssue.name}@${firstIssue.resolvedVersion ?? 'unknown'} does not satisfy ${firstIssue.required} (${firstIssue.staticUsage})`;
1136
+ return {
1137
+ result: 'peer-contract-incompatible',
1138
+ reason: `the exact artifact installed and loaded, but ${detail}`,
1139
+ };
1140
+ }
1141
+ if (graph.unresolved > 0) {
1142
+ return {
1143
+ result: 'unknown',
1144
+ reason: `the exact artifact installed and loaded, but the effective DSH runtime graph has ${graph.unresolved} required unresolved edge(s)`,
1145
+ };
1146
+ }
1147
+ if (contracts.indeterminate > 0) {
1148
+ return {
1149
+ result: 'unknown',
1150
+ reason: `the exact artifact installed and loaded, but ${contracts.indeterminate} required plugin peer range(s) could not be evaluated safely`,
1151
+ };
1152
+ }
1153
+ return {
1154
+ result: 'compatible',
1155
+ reason: 'the exact artifact installed, registered, loaded, and satisfied its direct peer contracts under the requested DSH version',
1156
+ };
1157
+ }
611
1158
  export async function observeDshPluginInstall(options) {
612
1159
  const spec = parseNpmSpec(options.packageSpec);
613
1160
  if (!EXACT_VERSION.test(options.dshVersion))
614
1161
  throw new Error('DSH version must be an exact semantic version');
1162
+ if (options.caseId !== undefined && !/^[a-z0-9][a-z0-9._-]{0,63}$/.test(options.caseId)) {
1163
+ throw new Error('DSH install observation caseId must be a short lowercase label');
1164
+ }
615
1165
  const allowedBuilds = normalizeAllowedBuilds(options.allowedBuilds);
616
1166
  if (!options.allowExecution)
617
1167
  throw new Error('DSH install observation requires explicit execution consent');
@@ -637,6 +1187,7 @@ export async function observeDshPluginInstall(options) {
637
1187
  scope: 'install-and-load-behavior',
638
1188
  startedAt,
639
1189
  completedAt: startedAt,
1190
+ ...(options.caseId === undefined ? {} : { caseId: options.caseId }),
640
1191
  dshVersion: options.dshVersion,
641
1192
  runtime: {
642
1193
  platform: process.platform,
@@ -660,6 +1211,7 @@ export async function observeDshPluginInstall(options) {
660
1211
  },
661
1212
  observations: { install: emptyTraceObservation(), load: emptyTraceObservation() },
662
1213
  filesystem: { install: emptyFilesystemDiff(), load: emptyFilesystemDiff() },
1214
+ resolution: {},
663
1215
  result: 'unknown',
664
1216
  reason: 'observation did not complete',
665
1217
  boundary: {
@@ -788,6 +1340,7 @@ export async function observeDshPluginInstall(options) {
788
1340
  const afterInstall = await snapshotSandbox(sandboxRoot, environment);
789
1341
  report.observations.install = await readTrace(installTracePath, sandboxRoot);
790
1342
  report.filesystem.install = diffSnapshots(beforeInstall, afterInstall);
1343
+ report.resolution = await resolutionEvidence(environment.DSH_HOME, spec, options.dshVersion, environment.XDG_CACHE_HOME);
791
1344
  report.stages.install = commandStage(installResult);
792
1345
  if (installResult.timedOut || installResult.outputExceeded || installResult.launchError !== undefined) {
793
1346
  return finishReport(report, 'unknown', 'the plugin install did not produce a bounded command result');
@@ -809,12 +1362,13 @@ export async function observeDshPluginInstall(options) {
809
1362
  : { status: 'failed', detail: `the DSH profile did not register ${spec.name}` };
810
1363
  if (!registered)
811
1364
  return finishReport(report, 'install-failed', 'DSH accepted the install command but did not register the plugin bundle');
1365
+ const loadProbe = await writeProfileLoadProbe(environment.DSH_HOME, spec.name, options.dshVersion, pnpmCommand, artifact.requiredPeerDependencies);
812
1366
  const loadTracePath = join(traceDirectory, 'load.strace');
813
1367
  const loadResult = await runSafely(runner, {
814
1368
  phase: 'load',
815
- command: pnpmCommand,
816
- args: dshArgs(options.dshVersion, ['--profile', PROFILE, '--dump-config']),
817
- cwd: artifactDirectory,
1369
+ command: process.execPath,
1370
+ args: [loadProbe.path],
1371
+ cwd: loadProbe.profileDirectory,
818
1372
  env: scriptsEnvironment,
819
1373
  timeoutMs,
820
1374
  sandboxRoot,
@@ -823,6 +1377,10 @@ export async function observeDshPluginInstall(options) {
823
1377
  const afterLoad = await snapshotSandbox(sandboxRoot, environment);
824
1378
  report.observations.load = await readTrace(loadTracePath, sandboxRoot);
825
1379
  report.filesystem.load = diffSnapshots(afterInstall, afterLoad);
1380
+ const profilePeerContracts = await readProfilePeerContracts(loadProbe.peerResolutionPath, artifact.requiredPeerDependencies, sandboxRoot);
1381
+ // Loading may trigger one final DSH profile reconciliation. Preserve the
1382
+ // final resolved graph rather than only the state immediately after add.
1383
+ report.resolution = await resolutionEvidence(environment.DSH_HOME, spec, options.dshVersion, environment.XDG_CACHE_HOME, profilePeerContracts);
826
1384
  report.stages.load = commandStage(loadResult);
827
1385
  if (loadResult.timedOut || loadResult.outputExceeded || loadResult.launchError !== undefined) {
828
1386
  return finishReport(report, 'unknown', 'the plugin load did not produce a bounded command result');
@@ -833,7 +1391,8 @@ export async function observeDshPluginInstall(options) {
833
1391
  }
834
1392
  if (loadResult.code !== 0)
835
1393
  return finishReport(report, 'load-failed', 'the traced DSH profile load command failed');
836
- return finishReport(report, 'compatible', 'the exact artifact installed, registered and loaded under the requested DSH version');
1394
+ const conclusion = finalCompatibilityConclusion(report.resolution);
1395
+ return finishReport(report, conclusion.result, conclusion.reason);
837
1396
  }
838
1397
  catch (error) {
839
1398
  const detail = bounded(error instanceof Error ? error.message : String(error));
@@ -854,6 +1413,7 @@ export function renderDshInstallObservation(report) {
854
1413
  const lifecycle = report.artifact.lifecycleScripts.length === 0 ? 'none' : report.artifact.lifecycleScripts.join(', ');
855
1414
  const lines = [
856
1415
  'DSH isolated install observation',
1416
+ ...(report.caseId === undefined ? [] : [`Case: ${report.caseId}`]),
857
1417
  `Artifact: ${report.artifact.name}@${report.artifact.version}${report.artifact.sha256 === undefined ? '' : ` (sha256:${report.artifact.sha256.slice(0, 12)}…)`}`,
858
1418
  `DSH: ${report.dshVersion}`,
859
1419
  `Runtime: Node ${report.runtime.nodeVersion} (${report.runtime.platform}/${report.runtime.architecture}), pnpm ${report.runtime.packageManager.version ?? 'unknown'}`,
@@ -866,11 +1426,24 @@ export function renderDshInstallObservation(report) {
866
1426
  `Install evidence: ${install.processes.length} process, ${install.network.length} network, ${install.fileWrites.length} file-write event(s); trace ${install.coverage.status}`,
867
1427
  `Load evidence: ${load.processes.length} process, ${load.network.length} network, ${load.fileWrites.length} file-write event(s); trace ${load.coverage.status}`,
868
1428
  `Final filesystem delta: install +${report.filesystem.install.totals.created} ~${report.filesystem.install.totals.modified} -${report.filesystem.install.totals.deleted}; load +${report.filesystem.load.totals.created} ~${report.filesystem.load.totals.modified} -${report.filesystem.load.totals.deleted}`,
1429
+ `Resolved profile graph: ${report.resolution.profileLockfile?.graphDigest ?? 'not established'}${report.resolution.profileLockfile === undefined ? '' : ` (lock sha256:${report.resolution.profileLockfile.sha256.slice(0, 12)}…)`}`,
1430
+ `Effective DSH runtime graph: ${report.resolution.runtimeGraph?.digest ?? 'not established'}${report.resolution.runtimeGraph === undefined ? '' : ` (${report.resolution.runtimeGraph.nodes} nodes, ${report.resolution.runtimeGraph.edges} edges, ${report.resolution.runtimeGraph.unresolved} required unresolved${report.resolution.runtimeGraph.optionalUnavailable === undefined ? '' : `, ${report.resolution.runtimeGraph.optionalUnavailable} optional unavailable`})`}`,
1431
+ `Plugin peer contracts: ${report.resolution.runtimeGraph === undefined
1432
+ ? 'not established'
1433
+ : `${report.resolution.runtimeGraph.pluginPeerContracts.satisfied}/${report.resolution.runtimeGraph.pluginPeerContracts.declared} satisfied; ${report.resolution.runtimeGraph.pluginPeerContracts.mismatched} mismatched, ${report.resolution.runtimeGraph.pluginPeerContracts.missing} missing, ${report.resolution.runtimeGraph.pluginPeerContracts.indeterminate} indeterminate`}`,
1434
+ `Effective graph collector: ${report.resolution.runtimeGraphError ?? 'captured'}`,
869
1435
  '',
870
1436
  ];
871
1437
  for (const [name, stage] of Object.entries(report.stages)) {
872
1438
  lines.push(` ${name}: ${stage.status}${stage.detail === undefined ? '' : ` (${stage.detail})`}`);
873
1439
  }
1440
+ const peerIssues = report.resolution.runtimeGraph?.pluginPeerContracts.issues ?? [];
1441
+ if (peerIssues.length > 0) {
1442
+ lines.push('', 'Direct peer-contract findings:');
1443
+ for (const issue of peerIssues) {
1444
+ lines.push(` ${issue.name}: ${issue.status}; requires ${issue.required}${issue.resolvedVersion === undefined ? '' : `, resolved ${issue.resolvedVersion}`}; static use ${issue.staticUsage}`);
1445
+ }
1446
+ }
874
1447
  lines.push('', report.boundary.note);
875
1448
  return `${lines.join('\n')}\n`;
876
1449
  }