proof-of-commitment 1.23.0 → 1.25.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 (3) hide show
  1. package/README.md +2 -2
  2. package/index.js +101 -20
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  An MCP server and web tool that scores npm packages, PyPI packages, Rust crates, Go modules, and GitHub repos on **behavioral commitment** — signals that are harder to fake than stars, READMEs, or download counts.
8
8
 
9
- ## The supply chain problem
9
+ ## The supply chain security problem
10
10
 
11
11
  26 of the 91 npm packages with >10M weekly downloads have a **single npm publisher**. Together they account for over 3 billion downloads per week. `npm audit` doesn't surface this. Stars don't either.
12
12
 
@@ -93,7 +93,7 @@ Add to Claude Desktop, Cursor, Windsurf, or any MCP-compatible AI tool. Then ask
93
93
 
94
94
  ## GitHub Action
95
95
 
96
- Add supply chain auditing to any CI pipeline in 30 seconds — auto-detects packages from `package.json` or `requirements.txt`, **posts results as a PR comment**, writes to GitHub Step Summary, and optionally fails on CRITICAL packages.
96
+ Add supply chain security auditing to any CI pipeline in 30 seconds — auto-detects packages from `package.json` or `requirements.txt`, **posts results as a PR comment**, writes to GitHub Step Summary, and optionally fails on CRITICAL packages.
97
97
 
98
98
  Use the dedicated action at [piiiico/commit-action](https://github.com/piiiico/commit-action):
99
99
 
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * proof-of-commitment CLI v1.23.0
3
+ * proof-of-commitment CLI v1.24.0
4
4
  * Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
5
5
  * Usage: npx proof-of-commitment [packages...] [options]
6
6
  */
@@ -101,6 +101,14 @@ async function handle429(res) {
101
101
  const retryAfter = Number.isFinite(data.retry_after_seconds)
102
102
  ? data.retry_after_seconds
103
103
  : null;
104
+ // Backend signals "you've blown past the free wall, Developer $15/mo is the
105
+ // right fix" via overshoot=true / tier_suggestion="developer" (added
106
+ // backend-side 2026-06-04). When set, backend routes instantKeyUrl to
107
+ // /pricing — so the CLI must NOT promise "Free API key in 30 seconds" or
108
+ // prompt for email (a 200/day key won't help someone scanning 260+/day).
109
+ // Mismatched CTA text + destination kills trust and conversion. This branch
110
+ // aligns label + URL + skips the inline email prompt. (Dogfood, 2026-06-06.)
111
+ const overshoot = data.overshoot === true || data.tier_suggestion === 'developer';
104
112
 
105
113
  // Forward-compat: if backend ever returns partial scoring on 429,
106
114
  // print what we have BEFORE the rescue message. Falls back to JSON
@@ -130,6 +138,28 @@ async function handle429(res) {
130
138
  }
131
139
  console.error('');
132
140
 
141
+ // Overshoot path: free key is the wrong tool. Surface a URL aligned with
142
+ // the backend's Developer recommendation, skip the email prompt, exit.
143
+ // Without this branch, the CLI would say "Free API key in 30 seconds (no
144
+ // card)" while the URL goes to /pricing — bait-and-switch that erodes
145
+ // trust at the highest-intent moment we get with a user.
146
+ if (overshoot) {
147
+ console.error(
148
+ clr(
149
+ c.cyan + c.bold,
150
+ ` → Compare plans (Developer $15/mo · 1,000/day · batch API): ${instantKeyUrl}`
151
+ )
152
+ );
153
+ if (retryAfter && retryAfter > 0) {
154
+ const hours = Math.floor(retryAfter / 3600);
155
+ const mins = Math.floor((retryAfter % 3600) / 60);
156
+ const resetIn = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
157
+ console.error(clr(c.dim, ` or wait — free-tier resets in ${resetIn} (00:00 UTC).`));
158
+ }
159
+ console.error('');
160
+ process.exit(1);
161
+ }
162
+
133
163
  // TTY: inline signup collapses the 6-step browser flow (visit URL → enter
134
164
  // email → copy key → switch back to terminal → export key → re-run) to a
135
165
  // single terminal prompt. Non-TTY (CI/piped) falls through to the URL.
@@ -383,10 +413,11 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
383
413
  console.log(clr(c.dim, `\n 📊 Monitor ${effectiveCritical === 1 ? 'this package' : 'these packages'}: `) +
384
414
  clr(c.cyan, `poc watch ${results.find(r => hasCritical(r.riskFlags))?.name || results[0]?.name}`));
385
415
  } else if (!process.stdin.isTTY || !process.stdout.isTTY) {
386
- // Non-TTY (CI, piped): show static URL since interactive prompt won't work
416
+ // Non-TTY (CI, piped): show one-step watch command since interactive prompt won't work
417
+ const watchPkg = results.find(r => hasCritical(r.riskFlags))?.name || results[0]?.name;
387
418
  console.log(clr(c.dim, `\n 📊 Monitor ${effectiveCritical === 1 ? 'this' : 'these ' + effectiveCritical} CRITICAL ${effectiveCritical === 1 ? 'package' : 'packages'} — get alerted when scores change.`));
388
- console.log(clr(c.dim, ' Get a free API key: ') + clr(c.cyan, 'https://getcommit.dev/get-started?utm_source=cli'));
389
- console.log(clr(c.dim, ' Then run: ') + clr(c.cyan, 'poc login'));
419
+ console.log(clr(c.dim, ' One step: ') + clr(c.cyan, `poc watch ${watchPkg} --email you@company.com`));
420
+ console.log(clr(c.dim, ' Free: 3 packages, weekly digest. Developer $15/mo: 15 packages, daily scans.'));
390
421
  }
391
422
  // else: TTY mode — inlineSignup() will prompt interactively after printTable
392
423
  } else if (!hasKey && (!process.stdin.isTTY || !process.stdout.isTTY)) {
@@ -396,8 +427,8 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
396
427
  // text only in CI/piped output where interactive prompts can't fire.
397
428
  // ref=audit-baseline distinguishes this funnel from audit-cli-429
398
429
  // (rate-limit rescue) and from the static utm_source=cli help-line.
399
- console.log(clr(c.dim, '\n 📊 Save this scan as your baseline. Re-run anytime with a free key:'));
400
- console.log(clr(c.dim, ' ') + clr(c.cyan, 'https://getcommit.dev/get-started?ref=audit-baseline&utm_source=cli') + clr(c.dim, ' (200/day free; push alerts on Developer $15/mo)'));
430
+ console.log(clr(c.dim, '\n 📊 Get alerted if any package degrades:'));
431
+ 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)'));
401
432
  }
402
433
  console.log();
403
434
  }
@@ -475,15 +506,15 @@ async function inlineSignup(results) {
475
506
  console.log(clr(c.dim, ` Backup sent to ${email}`));
476
507
  console.log();
477
508
  console.log(clr(c.bold, ' Next steps:'));
478
- console.log(clr(c.dim, ' • ') + clr(c.cyan, 'poc status') + clr(c.dim, ' — check your account'));
479
509
  // Surface a concrete watch target. CRITICAL first (highest urgency);
480
510
  // otherwise pick the lowest-score package as the most-likely-to-degrade.
481
511
  const watchTarget = critPkgs[0]?.name
482
512
  || results.slice().sort((a, b) => (a.score || 100) - (b.score || 100))[0]?.name;
483
513
  if (watchTarget) {
484
- console.log(clr(c.dim, ' • ') + clr(c.cyan, `poc watch ${watchTarget}`) + clr(c.dim, ' — start monitoring (Developer $15/mo)'));
514
+ console.log(clr(c.dim, ' • ') + clr(c.cyan, `poc watch ${watchTarget}`) + clr(c.dim, ' — monitor this package (free: 3 packages, weekly)'));
485
515
  }
486
516
  console.log(clr(c.dim, ' • ') + clr(c.cyan, 'poc init') + clr(c.dim, ' — add CI gate to this project'));
517
+ console.log(clr(c.dim, ' • ') + clr(c.cyan, 'poc status') + clr(c.dim, ' — check your account'));
487
518
  } else if (data.message) {
488
519
  console.log(clr(c.green, ` ✓ ${data.message}`));
489
520
  } else {
@@ -498,7 +529,7 @@ async function inlineSignup(results) {
498
529
 
499
530
  function printHelp() {
500
531
  console.log(`
501
- ${clr(c.bold, 'proof-of-commitment')} v1.23.0 — supply chain risk scorer
532
+ ${clr(c.bold, 'proof-of-commitment')} v1.24.0 — supply chain risk scorer
502
533
 
503
534
  ${clr(c.bold, 'Usage:')}
504
535
  npx proof-of-commitment Auto-detect manifest in current dir
@@ -536,9 +567,9 @@ ${clr(c.bold, 'Account:')}
536
567
  poc status Show current tier, usage, and limits
537
568
  poc logout Remove saved API key
538
569
 
539
- ${clr(c.bold, 'Monitoring (Developer $15/mo+):')}
540
- poc watch <package> [--ecosystem npm|pypi|cargo|golang]
541
- Add a package to daily monitoring
570
+ ${clr(c.bold, 'Monitoring (free: 3 packages weekly · Developer $15/mo: 15 daily):')}
571
+ poc watch <package> [--email you@co.com] [--ecosystem npm|pypi|cargo|golang]
572
+ Add a package to monitoring. --email creates a free key in one step.
542
573
  poc watchlist List monitored packages with current scores + risk
543
574
  poc unwatch <pkg> Remove a package from monitoring
544
575
 
@@ -1525,15 +1556,53 @@ async function printUpgradeRequired(res, campaign = 'watchlist-402') {
1525
1556
  /**
1526
1557
  * poc watch <package> [--ecosystem npm|pypi|cargo|golang]
1527
1558
  */
1528
- async function cmdWatch(pkg, ecosystem) {
1529
- const key = await readApiKey();
1559
+ async function cmdWatch(pkg, ecosystem, emailArg) {
1560
+ let key = await readApiKey();
1561
+
1562
+ // --email flag: create a free key inline if none exists, collapsing the
1563
+ // visit-site → enter-email → copy-key → poc-login → poc-watch flow to one step.
1564
+ if (!key && emailArg) {
1565
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailArg)) {
1566
+ console.error(clr(c.red, 'Invalid email. Usage: poc watch <pkg> --email you@co.com'));
1567
+ process.exit(1);
1568
+ }
1569
+ process.stdout.write(clr(c.dim, ' Creating free API key...'));
1570
+ try {
1571
+ const createRes = await fetch('https://poc-backend.amdal-dev.workers.dev/api/keys/create', {
1572
+ method: 'POST',
1573
+ headers: { 'Content-Type': 'application/json' },
1574
+ body: JSON.stringify({ email: emailArg, source: 'cli-watch' }),
1575
+ });
1576
+ const keyData = await createRes.json();
1577
+ if (keyData.key) {
1578
+ await writeApiKey(keyData.key);
1579
+ key = keyData.key;
1580
+ console.log(clr(c.green, ' ✓'));
1581
+ console.log(clr(c.dim, ` Key saved to ~/.commit/config. Backup sent to ${emailArg}.`));
1582
+ } else {
1583
+ const errMsg = keyData.error === 'rate_limit_exceeded'
1584
+ ? 'Too many keys from this IP today.'
1585
+ : (keyData.message || 'Could not create key.');
1586
+ console.error(clr(c.red, ` ${errMsg}`));
1587
+ process.exit(1);
1588
+ }
1589
+ } catch (err) {
1590
+ console.error(clr(c.red, ` Error: ${err.message}`));
1591
+ process.exit(1);
1592
+ }
1593
+ }
1594
+
1530
1595
  if (!key) {
1531
- console.error(clr(c.red, 'No API key found. Set COMMIT_API_KEY or add api_key=<key> to ~/.commit/config'));
1532
- console.error(clr(c.dim, 'Get a key at https://getcommit.dev/pricing?utm_source=cli'));
1596
+ console.error(clr(c.red, 'No API key found.'));
1597
+ console.error('');
1598
+ console.error(clr(c.bold, ' One-step setup — creates key + starts monitoring:'));
1599
+ console.error(clr(c.cyan, ` poc watch ${pkg} --email you@company.com`));
1600
+ console.error('');
1601
+ console.error(clr(c.dim, ' Or set COMMIT_API_KEY / add api_key=<key> to ~/.commit/config'));
1533
1602
  process.exit(1);
1534
1603
  }
1535
1604
 
1536
- process.stdout.write(clr(c.dim, `Adding ${pkg} (${ecosystem}) to watchlist...`));
1605
+ process.stdout.write(clr(c.dim, ` Adding ${pkg} (${ecosystem}) to watchlist...`));
1537
1606
  const res = await fetch(WATCHLIST_API, {
1538
1607
  method: 'POST',
1539
1608
  headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${key}` },
@@ -1541,6 +1610,16 @@ async function cmdWatch(pkg, ecosystem) {
1541
1610
  });
1542
1611
 
1543
1612
  if (res.status === 402) { process.stdout.write('\n'); await printUpgradeRequired(res, 'watch-cmd'); process.exit(1); }
1613
+ if (res.status === 422) {
1614
+ const errData = await res.json().catch(() => ({}));
1615
+ process.stdout.write('\n');
1616
+ console.log(clr(c.yellow, ` ⚠ ${errData.message || 'Package limit reached.'}`));
1617
+ if (errData.upgrade) {
1618
+ console.log(clr(c.dim, ` ${errData.upgrade.message || `Upgrade to ${errData.upgrade.plan} for more:`}`));
1619
+ console.log(clr(c.cyan, ` ${errData.upgrade.url}`));
1620
+ }
1621
+ process.exit(1);
1622
+ }
1544
1623
 
1545
1624
  const data = await res.json();
1546
1625
  if (!res.ok) {
@@ -1552,7 +1631,7 @@ async function cmdWatch(pkg, ecosystem) {
1552
1631
  process.stdout.write('\n');
1553
1632
  if (isNew) {
1554
1633
  console.log(clr(c.green, ` ✓ Now watching ${pkg}`));
1555
- console.log(clr(c.dim, ' Daily scan runs at 06:00 UTC. Alerts on score drop ≥10 or CRITICAL threshold.'));
1634
+ console.log(clr(c.dim, ' Weekly digest (Mondays). Upgrade to Developer ($15/mo) for daily scans + Slack alerts.'));
1556
1635
  } else {
1557
1636
  console.log(clr(c.dim, ` ↩ ${pkg} is already in your watchlist`));
1558
1637
  }
@@ -2147,15 +2226,17 @@ async function main() {
2147
2226
 
2148
2227
  if (subcmd === 'watch') {
2149
2228
  const pkg = args[1];
2150
- if (!pkg) { console.error('Usage: poc watch <package> [--ecosystem npm|pypi|cargo|golang]'); process.exit(1); }
2229
+ if (!pkg) { console.error('Usage: poc watch <package> [--email you@co.com] [--ecosystem npm|pypi|cargo|golang]'); process.exit(1); }
2151
2230
  let ecosystem = 'npm';
2231
+ let email = null;
2152
2232
  for (let i = 2; i < args.length; i++) {
2153
2233
  if (args[i] === '--ecosystem' || args[i] === '-e') ecosystem = args[++i] || 'npm';
2234
+ else if (args[i] === '--email') email = args[++i] || null;
2154
2235
  else if (args[i] === '--pypi') ecosystem = 'pypi';
2155
2236
  else if (args[i] === '--cargo') ecosystem = 'cargo';
2156
2237
  else if (args[i] === '--golang' || args[i] === '--go') ecosystem = 'golang';
2157
2238
  }
2158
- await cmdWatch(pkg, ecosystem);
2239
+ await cmdWatch(pkg, ecosystem, email);
2159
2240
  process.exit(0);
2160
2241
  }
2161
2242
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "proof-of-commitment",
3
- "version": "1.23.0",
3
+ "version": "1.25.0",
4
4
  "mcpName": "io.github.piiiico/proof-of-commitment",
5
- "description": "Supply chain risk scorer for npm, PyPI, Cargo, and Go packages — behavioral signals that can't be faked",
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",
7
7
  "bin": {
8
8
  "proof-of-commitment": "./index.js",
@@ -16,7 +16,9 @@
16
16
  ],
17
17
  "keywords": [
18
18
  "supply-chain",
19
+ "supply-chain-security",
19
20
  "security",
21
+ "scanner",
20
22
  "npm",
21
23
  "pypi",
22
24
  "cargo",