proof-of-commitment 1.31.1 → 1.32.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 (2) hide show
  1. package/index.js +70 -6
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * proof-of-commitment CLI v1.31.0
3
+ * proof-of-commitment CLI v1.32.0
4
4
  * Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
5
5
  * Usage: npx proof-of-commitment [packages...] [options]
6
6
  */
@@ -609,7 +609,22 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
609
609
  if (process.env.GITHUB_ACTIONS === 'true') {
610
610
  if (effectiveCritical > 0) {
611
611
  const critNames = results.filter(r => hasCritical(r.riskFlags)).slice(0, 5).map(r => r.name).join(', ');
612
- console.error(`::warning title=Commit: ${effectiveCritical} CRITICAL package${effectiveCritical > 1 ? 's' : ''}::Sole npm publisher + >10M downloads/week: ${critNames}. Details: getcommit.dev/audit?packages=${encodeURIComponent(critNames)}&utm_source=cli&utm_medium=ci-annotation`);
612
+ // PR-check annotation is the single user-visible artifact outside the raw
613
+ // CI log. It must point at the conversion destination (/get-started with
614
+ // watchlist auto-seed), not the viewer (/audit). Pre-fix the annotation
615
+ // linked to /audit?packages= — same data the CI viewer already saw
616
+ // scrolled up in the log + an inline signup form one extra scroll away.
617
+ // Post-fix the link routes directly to /get-started?watch=…&eco=… which
618
+ // seeds the user's watchlist from this scan and arrives at an email
619
+ // capture as the primary form. Mirrors the same `?watch=…` URL contract
620
+ // (test/get-started-watch-param.test.ts) used by /audit's bottom CTA
621
+ // and the AUR-1579 / Snyk-comparison / Miasma / IronWorm blog posts.
622
+ // ref=cli-watch (not cli-ci-critical) because the latter is not in the
623
+ // backend's VALID_SOURCES and would get coerced to "web" — destroying
624
+ // attribution. cli-watch is already an accepted source.
625
+ const watchUrl = buildWatchUrl(results, 'cli-watch');
626
+ const ctaUrl = watchUrl || `https://getcommit.dev/audit?packages=${encodeURIComponent(critNames)}&utm_source=cli&utm_medium=ci-annotation`;
627
+ console.error(`::warning title=Commit: ${effectiveCritical} CRITICAL package${effectiveCritical > 1 ? 's' : ''}::Sole npm publisher + >10M downloads/week: ${critNames}. Watch + alert if scores change: ${ctaUrl}`);
613
628
  }
614
629
  if (compromisedCount > 0) {
615
630
  const compNames = results.filter(r => r.compromised).slice(0, 5).map(r => r.name).join(', ');
@@ -650,10 +665,21 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
650
665
  console.log(clr(c.dim, `\n 📊 Monitor ${effectiveCritical === 1 ? 'this package' : 'these packages'}: `) +
651
666
  clr(c.cyan, `poc watch ${results.find(r => hasCritical(r.riskFlags))?.name || results[0]?.name}`));
652
667
  } else if (!process.stdin.isTTY || !process.stdout.isTTY) {
653
- // Non-TTY (CI, piped): show one-step watch command since interactive prompt won't work
668
+ // Non-TTY (CI, piped): show both a clickable signup URL AND the one-step
669
+ // watch command. URL is for CI viewers reading log output in the browser
670
+ // (PR check tabs auto-link URLs); CLI command is for local-pipe / script
671
+ // contexts where copying a URL to a browser is friction. Two paths to
672
+ // the same outcome — see buildWatchUrl docstring for the rationale.
673
+ // Pre-fix this branch only emitted the CLI command, which requires the
674
+ // user to know the package name, run a second `npx`, and remember an
675
+ // email — a 3-decision ask in a CI log line that scrolls past.
654
676
  const watchPkg = results.find(r => hasCritical(r.riskFlags))?.name || results[0]?.name;
677
+ const watchUrl = buildWatchUrl(results, 'cli-watch');
655
678
  console.log(clr(c.dim, `\n 📊 Monitor ${effectiveCritical === 1 ? 'this' : 'these ' + effectiveCritical} CRITICAL ${effectiveCritical === 1 ? 'package' : 'packages'} — get alerted when scores change.`));
656
- console.log(clr(c.dim, ' One step: ') + clr(c.cyan, `poc watch ${watchPkg} --email you@company.com`));
679
+ if (watchUrl) {
680
+ console.log(clr(c.dim, ' Web: ') + clr(c.cyan, watchUrl));
681
+ }
682
+ console.log(clr(c.dim, ' CLI: ') + clr(c.cyan, `poc watch ${watchPkg} --email you@company.com`));
657
683
  console.log(clr(c.dim, ' Free: 3 packages, weekly digest. Developer $15/mo: 15 packages, daily scans.'));
658
684
  }
659
685
  // else: TTY mode — inlineSignup() will prompt interactively after printTable
@@ -664,8 +690,17 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
664
690
  // text only in CI/piped output where interactive prompts can't fire.
665
691
  // ref=audit-baseline distinguishes this funnel from audit-cli-429
666
692
  // (rate-limit rescue) and from the static utm_source=cli help-line.
693
+ // Healthy-results non-TTY (CI, piped, no saved key) CTA. Mirror the
694
+ // CRITICAL branch's dual-path (Web + CLI). ref=audit-baseline keeps this
695
+ // funnel distinguishable from CRITICAL-driven cli-watch in api_keys.source
696
+ // breakdowns — important for measuring whether degradation-alert framing
697
+ // converts at all on clean results, separate from CRITICAL conversions.
698
+ const watchUrl = buildWatchUrl(results, 'audit-baseline');
667
699
  console.log(clr(c.dim, '\n 📊 Get alerted if any package degrades:'));
668
- console.log(clr(c.dim, ' ') + clr(c.cyan, `poc watch ${results[0]?.name || '<package>'} --email you@company.com`) + clr(c.dim, ' (free: 3 packages, weekly digest)'));
700
+ if (watchUrl) {
701
+ console.log(clr(c.dim, ' Web: ') + clr(c.cyan, watchUrl));
702
+ }
703
+ console.log(clr(c.dim, ' CLI: ') + clr(c.cyan, `poc watch ${results[0]?.name || '<package>'} --email you@company.com`) + clr(c.dim, ' (free: 3 packages, weekly digest)'));
669
704
  }
670
705
  console.log();
671
706
  }
@@ -703,6 +738,35 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
703
738
  * Closes the second proposition-gap layer (CLI-side mirror of the
704
739
  * 2026-06-11 audit-page watchlist auto-seed at abe53f1/df8a8be).
705
740
  */
741
+ /**
742
+ * Build a clickable /get-started URL that pre-seeds the watchlist from the
743
+ * scanned packages. Mirrors the `?watch=pkg1,pkg2,pkg3&eco=npm` URL contract
744
+ * pinned by test/get-started-watch-param.test.ts. The free-tier cap (3) is
745
+ * enforced via buildCliWatchSeeds(); /get-started.astro re-validates and the
746
+ * backend re-caps at PACKAGE_LIMITS.free (defense in depth across drift).
747
+ *
748
+ * Why this exists: the proven Snyk-comparison/AUR-1579 blog conversion pattern
749
+ * (mid-essay dual CTA + bottom dual CTA + `?watch=…` pre-seed) was sitting
750
+ * one repo over from the CLI for weeks. The CLI emits the highest-intent
751
+ * touchpoint after npm install — a CRITICAL finding in a real user's tree —
752
+ * but the only conversion paths it offered non-TTY users were a multi-token
753
+ * `poc watch X --email Y` CLI command (high friction) and a `getcommit.dev/
754
+ * audit?packages=…` link that drops users on the viewer page (not a converter).
755
+ * This helper produces the clickable conversion URL with packages pre-seeded.
756
+ *
757
+ * Returns null when no valid seeds exist (e.g. empty results, or all names
758
+ * fail the npm-name regex via buildCliWatchSeeds). Callers degrade gracefully
759
+ * to text-only CTAs without a URL.
760
+ */
761
+ function buildWatchUrl(results, ref) {
762
+ const seeds = buildCliWatchSeeds(results);
763
+ if (seeds.length === 0) return null;
764
+ // All seeds from one scan share the same ecosystem; pick from the first.
765
+ const eco = seeds[0].ecosystem || 'npm';
766
+ const names = seeds.map(s => s.name).join(',');
767
+ return `https://getcommit.dev/get-started?watch=${encodeURIComponent(names)}&eco=${eco}&ref=${ref}&utm_source=cli`;
768
+ }
769
+
706
770
  function buildCliWatchSeeds(results) {
707
771
  if (!Array.isArray(results) || results.length === 0) return [];
708
772
  const VALID_ECOS = new Set(['npm', 'pypi', 'cargo', 'golang']);
@@ -878,7 +942,7 @@ async function inlineSignup(results, opts = {}) {
878
942
 
879
943
  function printHelp() {
880
944
  console.log(`
881
- ${clr(c.bold, 'proof-of-commitment')} v1.31.0 — supply chain risk scorer
945
+ ${clr(c.bold, 'proof-of-commitment')} v1.32.0 — supply chain risk scorer
882
946
 
883
947
  ${clr(c.bold, 'Usage:')}
884
948
  npx proof-of-commitment Auto-detect manifest in current dir
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proof-of-commitment",
3
- "version": "1.31.1",
3
+ "version": "1.32.0",
4
4
  "mcpName": "io.github.piiiico/proof-of-commitment",
5
5
  "description": "Supply chain security risk scorer for npm, PyPI, Cargo, and Go packages — behavioral signals that can't be faked",
6
6
  "type": "module",