devmap 0.3.5__py3-none-any.whl → 0.3.6__py3-none-any.whl

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.
devmap/_devmap.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  // @bun
3
3
 
4
4
  // src/version.ts
5
- var VERSION = "0.3.5";
5
+ var VERSION = "0.3.6";
6
6
 
7
7
  // src/install.ts
8
8
  function isIsolated(i) {
@@ -662,6 +662,138 @@ function runScaAudit(cwd) {
662
662
  return checks;
663
663
  }
664
664
 
665
+ // src/health/parse.ts
666
+ function parsePipCheck(text) {
667
+ const lines = (text || "").split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
668
+ const clean = lines.some((l) => /^no broken requirements found/i.test(l));
669
+ const conflicts = lines.filter((l) => !/^no broken requirements found/i.test(l));
670
+ if (clean || conflicts.length === 0) {
671
+ return { status: "ok", summary: "pip dependencies consistent" };
672
+ }
673
+ return {
674
+ status: "warn",
675
+ summary: `pip has ${conflicts.length} broken dependency requirement${conflicts.length === 1 ? "" : "s"}`,
676
+ detail: conflicts.join(`
677
+ `)
678
+ };
679
+ }
680
+ function parseNpmDoctor(text) {
681
+ const t = (text || "").trim();
682
+ if (!t)
683
+ return { status: "skipped", summary: "npm doctor produced no output (timed out or silent)" };
684
+ const lines = t.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
685
+ const NET_RE = /ECONNREFUSED|ETIMEDOUT|ENOTFOUND|getaddrinfo|EAI_|network request|registry\.npmjs\.org|request to https?:/i;
686
+ const BOILER_RE = /complete log of this run|^\s+at\s|node:internal|file:\/\//i;
687
+ const netLines = lines.filter((l) => NET_RE.test(l));
688
+ const failLines = lines.filter((l) => /npm error|✗|✖/i.test(l) && !BOILER_RE.test(l));
689
+ const realFails = failLines.filter((l) => !NET_RE.test(l));
690
+ if (realFails.length) {
691
+ return {
692
+ status: "warn",
693
+ summary: `npm doctor reported ${realFails.length} issue${realFails.length === 1 ? "" : "s"}`,
694
+ detail: realFails.join(`
695
+ `)
696
+ };
697
+ }
698
+ if (netLines.length) {
699
+ return { status: "skipped", summary: "npm doctor skipped (registry/network unreachable)" };
700
+ }
701
+ return { status: "ok", summary: "npm doctor passed" };
702
+ }
703
+ function parseRustupCheck(text) {
704
+ const t = (text || "").trim();
705
+ if (!t)
706
+ return { status: "skipped", summary: "rustup check produced no output (timed out or silent)" };
707
+ const lines = t.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
708
+ const updates = lines.filter((l) => /update available/i.test(l));
709
+ const upToDate = lines.filter((l) => /up to date/i.test(l));
710
+ if (updates.length) {
711
+ return {
712
+ status: "info",
713
+ summary: `rustup: ${updates.length} toolchain update${updates.length === 1 ? "" : "s"} available`,
714
+ detail: updates.join(`
715
+ `)
716
+ };
717
+ }
718
+ if (upToDate.length)
719
+ return { status: "ok", summary: "rustup toolchains up to date" };
720
+ return { status: "ok", summary: "rustup check passed" };
721
+ }
722
+
723
+ // src/health/scan.ts
724
+ var PROBES = [
725
+ {
726
+ id: "pip",
727
+ label: "pip",
728
+ kind: "conflict",
729
+ network: false,
730
+ candidates: [
731
+ ["pip", ["check"]],
732
+ ["pip3", ["check"]],
733
+ ["python", ["-m", "pip", "check"]],
734
+ ["python3", ["-m", "pip", "check"]]
735
+ ],
736
+ timeout: 15000,
737
+ parse: parsePipCheck
738
+ },
739
+ {
740
+ id: "npm",
741
+ label: "npm",
742
+ kind: "mismatch",
743
+ network: true,
744
+ candidates: [["npm", ["doctor"]]],
745
+ timeout: 20000,
746
+ parse: parseNpmDoctor
747
+ },
748
+ {
749
+ id: "rustup",
750
+ label: "rustup",
751
+ kind: "stale",
752
+ network: true,
753
+ candidates: [["rustup", ["check"]]],
754
+ timeout: 15000,
755
+ parse: parseRustupCheck
756
+ }
757
+ ];
758
+ function pickCandidate(p) {
759
+ const dirs = pathDirs();
760
+ for (const [cmd, args] of p.candidates) {
761
+ if (whichInDirs(cmd, dirs).length > 0)
762
+ return [cmd, args];
763
+ }
764
+ return null;
765
+ }
766
+ var SEV_OF = {
767
+ ok: "ok",
768
+ warn: "warn",
769
+ info: "info",
770
+ skipped: "info"
771
+ };
772
+ function toCheck(p, r) {
773
+ const sev = SEV_OF[r.status];
774
+ const id = `health.${p.id}.check`;
775
+ const realId = r.status === "skipped" ? `health.${p.id}.skipped` : id;
776
+ return makeCheck(realId, p.kind, sev, r.summary, r.detail || "", [], r.status === "ok" ? undefined : undefined);
777
+ }
778
+ async function computeHealth(opts = {}) {
779
+ const checks = [];
780
+ const network = opts.healthNetwork === true;
781
+ for (const p of PROBES) {
782
+ const picked = pickCandidate(p);
783
+ if (!picked)
784
+ continue;
785
+ if (p.network && !network) {
786
+ checks.push(makeCheck(`health.${p.id}.skipped`, p.kind, "info", `${p.label} doctor skipped (offline by default)`, `Run with --health-network to invoke ${p.label} self-checks (they require network).`));
787
+ continue;
788
+ }
789
+ const [cmd, args] = picked;
790
+ const out = await runAsync(cmd, args, p.timeout);
791
+ const result = p.parse(out);
792
+ checks.push(toCheck(p, result));
793
+ }
794
+ return checks;
795
+ }
796
+
665
797
  // src/output/json.ts
666
798
  function buildManifest(byName, conflicts, checks, environments, shells) {
667
799
  const sys = byName.system || {};
@@ -841,7 +973,8 @@ async function runScan(shells, disabled, opts = {}) {
841
973
  progress,
842
974
  checkOutdated: opts.checkOutdated,
843
975
  wslDeep: opts.wslDeep,
844
- auditDeps: opts.auditDeps
976
+ auditDeps: opts.auditDeps,
977
+ healthNetwork: opts.healthNetwork
845
978
  };
846
979
  const { byName, allChecks } = await runDetectors(ctx, progress);
847
980
  const groups = [
@@ -850,12 +983,13 @@ async function runScan(shells, disabled, opts = {}) {
850
983
  ...byName.ides || []
851
984
  ];
852
985
  const conflicts = computeConflicts(groups, shells, disabled);
986
+ const healthChecks = await computeHealth({ healthNetwork: opts.healthNetwork });
853
987
  const securityChecks = [
854
988
  ...computeSecurity(byName),
855
989
  ...computeScaInventory(),
856
990
  ...opts.auditDeps ? runScaAudit(process.cwd()) : []
857
991
  ];
858
- const collected = [...allChecks, ...conflicts.checks, ...securityChecks].filter((c) => !isCheckDisabled(c.ID, disabled));
992
+ const collected = [...allChecks, ...conflicts.checks, ...securityChecks, ...healthChecks].filter((c) => !isCheckDisabled(c.ID, disabled));
859
993
  const environments = {
860
994
  vcpkg: byName.vcpkg,
861
995
  pip: byName.pip,
@@ -1906,7 +2040,8 @@ async function cmdScan(opts, pos) {
1906
2040
  silent: opts.quiet === true,
1907
2041
  checkOutdated: opts.outdated === true,
1908
2042
  wslDeep: opts["wsl-deep"] === true,
1909
- auditDeps: opts["audit-deps"] === true
2043
+ auditDeps: opts["audit-deps"] === true,
2044
+ healthNetwork: opts["health-network"] === true
1910
2045
  });
1911
2046
  if (opts.json) {
1912
2047
  emitText(JSON.stringify(manifest, null, 2), opts.bom === true);
@@ -2193,6 +2328,7 @@ scan options:
2193
2328
  --wsl-deep probe inside stopped WSL distros (default probes only running ones to avoid waking distros)
2194
2329
  --ci CI gate: clean → exit 0; any warn → exit 2; any error → exit 1
2195
2330
  --audit-deps dependency CVE audit via OSV-Scanner/pip-audit/govulncheck/Trivy (opt-in, uses network)
2331
+ --health-network run network health probes too (npm doctor / rustup check; off by default to stay offline-fast)
2196
2332
  --quiet suppress scan progress feedback (for scripts/CI)
2197
2333
  --bom prepend UTF-8 BOM for viewing in Notepad etc. (only on redirect/pipe)
2198
2334
  --version / --help
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devmap
3
- Version: 0.3.5
3
+ Version: 0.3.6
4
4
  Summary: Map your machine's dev environment and surface issues (PATH conflicts, ghost binaries, multi-version collisions). fastfetch-for-dev-tools. This PyPI package ships the real devmap CLI (a bundled JavaScript app launched via Node.js or Bun), so `pip install devmap && devmap` works standalone.
5
5
  Author: devmap contributors
6
6
  License: MIT
@@ -40,6 +40,7 @@ Requirements at runtime: a Node.js (or Bun) runtime available on `PATH`. Python
40
40
  - **Off-PATH awareness**: reports installed-but-not-on-PATH tools, while correctly treating version-manager environments (conda envs, nvm versions) as by-design off-PATH.
41
41
  - **Environment layers**: vcpkg packages, per-python pip packages (incl. upgradable), apt packages (Linux), conda environments, Docker (binary + daemon + images/containers), WSL distros, Visual Studio dev shells, and project-local environments (`.venv`, `node_modules`).
42
42
  - **Dependency auditing (optional)**: with `--audit-deps`, devmap detects and invokes installed SCA tools (osv-scanner, govulncheck, pip-audit, Trivy), parses their JSON, and merges results into a unified view. devmap is a *scanner/orchestrator*, not a CVE database — for authoritative vulnerability data, use those tools directly.
43
+ - **Health layer**: converges official self-checks into one view — `pip check` (broken dependency requirements, **offline by default**) and, with `--health-network`, `npm doctor` / `rustup check`. Unavailable or offline probes degrade gracefully to `info`.
43
44
  - **Three sub-commands**: `scan` (the fact source), `query` (ask the manifest), `devdiff` (compare two manifests — environment drift / cross-machine consistency).
44
45
  - **Zero Python dependencies.** The bundled app is pure TypeScript compiled to JavaScript.
45
46
 
@@ -0,0 +1,7 @@
1
+ devmap/__init__.py,sha256=4Ek7Di0FYYl76FKnHJ8VYiJPHlUTfoqyBB_hul_pFbk,1728
2
+ devmap/_devmap.mjs,sha256=kYiHodxsTRu90Anb1QtKk_llF1-Q21S4N9KrLuHgQsw,84108
3
+ devmap-0.3.6.dist-info/METADATA,sha256=J89bwvXkn1xoRuyarTu7e1LQgQyCohhgIVylFrS21nI,6796
4
+ devmap-0.3.6.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
5
+ devmap-0.3.6.dist-info/entry_points.txt,sha256=FOQKZCLz0WGWemrTw0suYRqKWrUB4_XJ2Gkrw60ZEcg,39
6
+ devmap-0.3.6.dist-info/top_level.txt,sha256=QeomiD_JKcjSd_jTZv0WJF3darUb2sUsxmocjgYJ6Qc,7
7
+ devmap-0.3.6.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- devmap/__init__.py,sha256=4Ek7Di0FYYl76FKnHJ8VYiJPHlUTfoqyBB_hul_pFbk,1728
2
- devmap/_devmap.mjs,sha256=xRTpuwBHdOAWySpx4TEudvxS0aIA288NYmluaXyoEAw,79539
3
- devmap-0.3.5.dist-info/METADATA,sha256=mwnfHI_6L1uNkqqjinecVYDiy0vtTWSS0fguLrtLk5s,6536
4
- devmap-0.3.5.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
5
- devmap-0.3.5.dist-info/entry_points.txt,sha256=FOQKZCLz0WGWemrTw0suYRqKWrUB4_XJ2Gkrw60ZEcg,39
6
- devmap-0.3.5.dist-info/top_level.txt,sha256=QeomiD_JKcjSd_jTZv0WJF3darUb2sUsxmocjgYJ6Qc,7
7
- devmap-0.3.5.dist-info/RECORD,,
File without changes