proof-of-commitment 1.24.0 → 1.25.1
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.
- package/README.md +7 -7
- package/index.js +125 -20
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -11,15 +11,15 @@ An MCP server and web tool that scores npm packages, PyPI packages, Rust crates,
|
|
|
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
|
|
|
13
13
|
Four packages in a typical Node.js project are CRITICAL right now:
|
|
14
|
-
- **chalk** —
|
|
15
|
-
- **zod** —
|
|
16
|
-
- **lodash** —
|
|
17
|
-
- **axios** —
|
|
14
|
+
- **chalk** — 432M downloads/week, **1 npm publisher**
|
|
15
|
+
- **zod** — 185M downloads/week, **1 npm publisher** (30+ GitHub contributors)
|
|
16
|
+
- **lodash** — 156M downloads/week, **1 npm publisher**
|
|
17
|
+
- **axios** — 113M downloads/week, **1 npm publisher** (attacked March 30, 2026)
|
|
18
18
|
|
|
19
19
|
They won't appear in your `package.json` either — but these are in almost every project:
|
|
20
|
-
- **minimatch** —
|
|
21
|
-
- **glob** —
|
|
22
|
-
- **cross-spawn** —
|
|
20
|
+
- **minimatch** — 625M downloads/week, **1 npm publisher**
|
|
21
|
+
- **glob** — 366M downloads/week, **1 npm publisher**
|
|
22
|
+
- **cross-spawn** — 215M downloads/week, **1 npm publisher**
|
|
23
23
|
|
|
24
24
|
Behavioral signals surface this. Stars and READMEs don't.
|
|
25
25
|
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* proof-of-commitment CLI v1.
|
|
3
|
+
* proof-of-commitment CLI v1.25.1
|
|
4
4
|
* Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
|
|
5
5
|
* Usage: npx proof-of-commitment [packages...] [options]
|
|
6
6
|
*/
|
|
@@ -98,9 +98,14 @@ async function handle429(res) {
|
|
|
98
98
|
const partial = Array.isArray(data.packages_already_scored)
|
|
99
99
|
? data.packages_already_scored
|
|
100
100
|
: [];
|
|
101
|
+
// Authenticated keys: retry_after (seconds, used by worker auth-middleware quota path).
|
|
102
|
+
// Anonymous IPs: retry_after_seconds (legacy / overshoot rescue path).
|
|
103
|
+
// Read both so both paths surface a reset-time hint.
|
|
101
104
|
const retryAfter = Number.isFinite(data.retry_after_seconds)
|
|
102
105
|
? data.retry_after_seconds
|
|
103
|
-
:
|
|
106
|
+
: Number.isFinite(data.retry_after)
|
|
107
|
+
? data.retry_after
|
|
108
|
+
: null;
|
|
104
109
|
// Backend signals "you've blown past the free wall, Developer $15/mo is the
|
|
105
110
|
// right fix" via overshoot=true / tier_suggestion="developer" (added
|
|
106
111
|
// backend-side 2026-06-04). When set, backend routes instantKeyUrl to
|
|
@@ -109,6 +114,25 @@ async function handle429(res) {
|
|
|
109
114
|
// Mismatched CTA text + destination kills trust and conversion. This branch
|
|
110
115
|
// aligns label + URL + skips the inline email prompt. (Dogfood, 2026-06-06.)
|
|
111
116
|
const overshoot = data.overshoot === true || data.tier_suggestion === 'developer';
|
|
117
|
+
// Authenticated-key quota path (added 2026-06-10): when the user already
|
|
118
|
+
// owns an API key and burns through their daily allowance, the backend
|
|
119
|
+
// auth-middleware (worker.ts resolveApiKey) returns a NESTED upgrade object:
|
|
120
|
+
// { error, message, tier, upgrade: { url, plan, price, limit, message }, retry_after }
|
|
121
|
+
// The legacy handle429() shape only knew the FLAT anonymous-IP shape
|
|
122
|
+
// (instant_key_url, upgrade_url, overshoot, tier_suggestion). On a free-tier
|
|
123
|
+
// key quota hit, all those flat fields were undefined → handler fell back
|
|
124
|
+
// to "Get a free key" + inline email prompt → user (who already has a key)
|
|
125
|
+
// got bait-and-switched at their highest-intent moment: invested in setup,
|
|
126
|
+
// used the key all day, ready to upgrade — and we offered them to create
|
|
127
|
+
// ANOTHER free key. Detect via `data.upgrade?.url && data.upgrade?.plan` +
|
|
128
|
+
// a non-anonymous `data.tier`, route to dedicated upgrade UX. Aligns CLI
|
|
129
|
+
// CTA + URL + skips email prompt symmetric to the overshoot branch.
|
|
130
|
+
const keyUpgrade =
|
|
131
|
+
data.upgrade &&
|
|
132
|
+
typeof data.upgrade.url === 'string' &&
|
|
133
|
+
typeof data.upgrade.plan === 'string' &&
|
|
134
|
+
typeof data.tier === 'string' &&
|
|
135
|
+
data.tier !== 'anonymous';
|
|
112
136
|
|
|
113
137
|
// Forward-compat: if backend ever returns partial scoring on 429,
|
|
114
138
|
// print what we have BEFORE the rescue message. Falls back to JSON
|
|
@@ -138,6 +162,36 @@ async function handle429(res) {
|
|
|
138
162
|
}
|
|
139
163
|
console.error('');
|
|
140
164
|
|
|
165
|
+
// Authenticated-key quota path: user already has a key, hit their daily
|
|
166
|
+
// allowance. Free-key inline prompt is the wrong tool — surface upgrade.
|
|
167
|
+
// (Diagnosis: 2026-06-10 idle-mode dogfood — see comment block above.)
|
|
168
|
+
if (keyUpgrade) {
|
|
169
|
+
const planLabel = data.upgrade.plan.charAt(0).toUpperCase() + data.upgrade.plan.slice(1);
|
|
170
|
+
const price = data.upgrade.price || '';
|
|
171
|
+
const limit = data.upgrade.limit || '';
|
|
172
|
+
const pitch = data.upgrade.message || `Upgrade to ${planLabel}.`;
|
|
173
|
+
// URL already carries utm_campaign=key-upgrade + utm_source=key +
|
|
174
|
+
// utm_medium=quota from backend buildUpgradeUrl — no rewrite needed,
|
|
175
|
+
// /pricing key-upgrade banner reads these and pre-selects the tier.
|
|
176
|
+
console.error(
|
|
177
|
+
clr(
|
|
178
|
+
c.cyan + c.bold,
|
|
179
|
+
` → ${planLabel} (${price}${limit ? ' · ' + limit : ''}): ${data.upgrade.url}`
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
if (pitch) {
|
|
183
|
+
console.error(clr(c.dim, ` ${pitch}`));
|
|
184
|
+
}
|
|
185
|
+
if (retryAfter && retryAfter > 0) {
|
|
186
|
+
const hours = Math.floor(retryAfter / 3600);
|
|
187
|
+
const mins = Math.floor((retryAfter % 3600) / 60);
|
|
188
|
+
const resetIn = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
|
|
189
|
+
console.error(clr(c.dim, ` or wait — your free-tier quota resets in ${resetIn}.`));
|
|
190
|
+
}
|
|
191
|
+
console.error('');
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
|
|
141
195
|
// Overshoot path: free key is the wrong tool. Surface a URL aligned with
|
|
142
196
|
// the backend's Developer recommendation, skip the email prompt, exit.
|
|
143
197
|
// Without this branch, the CLI would say "Free API key in 30 seconds (no
|
|
@@ -413,10 +467,11 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
413
467
|
console.log(clr(c.dim, `\n 📊 Monitor ${effectiveCritical === 1 ? 'this package' : 'these packages'}: `) +
|
|
414
468
|
clr(c.cyan, `poc watch ${results.find(r => hasCritical(r.riskFlags))?.name || results[0]?.name}`));
|
|
415
469
|
} else if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
416
|
-
// Non-TTY (CI, piped): show
|
|
470
|
+
// Non-TTY (CI, piped): show one-step watch command since interactive prompt won't work
|
|
471
|
+
const watchPkg = results.find(r => hasCritical(r.riskFlags))?.name || results[0]?.name;
|
|
417
472
|
console.log(clr(c.dim, `\n 📊 Monitor ${effectiveCritical === 1 ? 'this' : 'these ' + effectiveCritical} CRITICAL ${effectiveCritical === 1 ? 'package' : 'packages'} — get alerted when scores change.`));
|
|
418
|
-
console.log(clr(c.dim, '
|
|
419
|
-
console.log(clr(c.dim, '
|
|
473
|
+
console.log(clr(c.dim, ' One step: ') + clr(c.cyan, `poc watch ${watchPkg} --email you@company.com`));
|
|
474
|
+
console.log(clr(c.dim, ' Free: 3 packages, weekly digest. Developer $15/mo: 15 packages, daily scans.'));
|
|
420
475
|
}
|
|
421
476
|
// else: TTY mode — inlineSignup() will prompt interactively after printTable
|
|
422
477
|
} else if (!hasKey && (!process.stdin.isTTY || !process.stdout.isTTY)) {
|
|
@@ -426,8 +481,8 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
426
481
|
// text only in CI/piped output where interactive prompts can't fire.
|
|
427
482
|
// ref=audit-baseline distinguishes this funnel from audit-cli-429
|
|
428
483
|
// (rate-limit rescue) and from the static utm_source=cli help-line.
|
|
429
|
-
console.log(clr(c.dim, '\n 📊
|
|
430
|
-
console.log(clr(c.dim, ' ') + clr(c.cyan, '
|
|
484
|
+
console.log(clr(c.dim, '\n 📊 Get alerted if any package degrades:'));
|
|
485
|
+
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)'));
|
|
431
486
|
}
|
|
432
487
|
console.log();
|
|
433
488
|
}
|
|
@@ -505,15 +560,15 @@ async function inlineSignup(results) {
|
|
|
505
560
|
console.log(clr(c.dim, ` Backup sent to ${email}`));
|
|
506
561
|
console.log();
|
|
507
562
|
console.log(clr(c.bold, ' Next steps:'));
|
|
508
|
-
console.log(clr(c.dim, ' • ') + clr(c.cyan, 'poc status') + clr(c.dim, ' — check your account'));
|
|
509
563
|
// Surface a concrete watch target. CRITICAL first (highest urgency);
|
|
510
564
|
// otherwise pick the lowest-score package as the most-likely-to-degrade.
|
|
511
565
|
const watchTarget = critPkgs[0]?.name
|
|
512
566
|
|| results.slice().sort((a, b) => (a.score || 100) - (b.score || 100))[0]?.name;
|
|
513
567
|
if (watchTarget) {
|
|
514
|
-
console.log(clr(c.dim, ' • ') + clr(c.cyan, `poc watch ${watchTarget}`) + clr(c.dim, ' —
|
|
568
|
+
console.log(clr(c.dim, ' • ') + clr(c.cyan, `poc watch ${watchTarget}`) + clr(c.dim, ' — monitor this package (free: 3 packages, weekly)'));
|
|
515
569
|
}
|
|
516
570
|
console.log(clr(c.dim, ' • ') + clr(c.cyan, 'poc init') + clr(c.dim, ' — add CI gate to this project'));
|
|
571
|
+
console.log(clr(c.dim, ' • ') + clr(c.cyan, 'poc status') + clr(c.dim, ' — check your account'));
|
|
517
572
|
} else if (data.message) {
|
|
518
573
|
console.log(clr(c.green, ` ✓ ${data.message}`));
|
|
519
574
|
} else {
|
|
@@ -566,9 +621,9 @@ ${clr(c.bold, 'Account:')}
|
|
|
566
621
|
poc status Show current tier, usage, and limits
|
|
567
622
|
poc logout Remove saved API key
|
|
568
623
|
|
|
569
|
-
${clr(c.bold, 'Monitoring (Developer $15/mo
|
|
570
|
-
poc watch <package> [--ecosystem npm|pypi|cargo|golang]
|
|
571
|
-
Add a package to
|
|
624
|
+
${clr(c.bold, 'Monitoring (free: 3 packages weekly · Developer $15/mo: 15 daily):')}
|
|
625
|
+
poc watch <package> [--email you@co.com] [--ecosystem npm|pypi|cargo|golang]
|
|
626
|
+
Add a package to monitoring. --email creates a free key in one step.
|
|
572
627
|
poc watchlist List monitored packages with current scores + risk
|
|
573
628
|
poc unwatch <pkg> Remove a package from monitoring
|
|
574
629
|
|
|
@@ -1555,15 +1610,53 @@ async function printUpgradeRequired(res, campaign = 'watchlist-402') {
|
|
|
1555
1610
|
/**
|
|
1556
1611
|
* poc watch <package> [--ecosystem npm|pypi|cargo|golang]
|
|
1557
1612
|
*/
|
|
1558
|
-
async function cmdWatch(pkg, ecosystem) {
|
|
1559
|
-
|
|
1613
|
+
async function cmdWatch(pkg, ecosystem, emailArg) {
|
|
1614
|
+
let key = await readApiKey();
|
|
1615
|
+
|
|
1616
|
+
// --email flag: create a free key inline if none exists, collapsing the
|
|
1617
|
+
// visit-site → enter-email → copy-key → poc-login → poc-watch flow to one step.
|
|
1618
|
+
if (!key && emailArg) {
|
|
1619
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailArg)) {
|
|
1620
|
+
console.error(clr(c.red, 'Invalid email. Usage: poc watch <pkg> --email you@co.com'));
|
|
1621
|
+
process.exit(1);
|
|
1622
|
+
}
|
|
1623
|
+
process.stdout.write(clr(c.dim, ' Creating free API key...'));
|
|
1624
|
+
try {
|
|
1625
|
+
const createRes = await fetch('https://poc-backend.amdal-dev.workers.dev/api/keys/create', {
|
|
1626
|
+
method: 'POST',
|
|
1627
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1628
|
+
body: JSON.stringify({ email: emailArg, source: 'cli-watch' }),
|
|
1629
|
+
});
|
|
1630
|
+
const keyData = await createRes.json();
|
|
1631
|
+
if (keyData.key) {
|
|
1632
|
+
await writeApiKey(keyData.key);
|
|
1633
|
+
key = keyData.key;
|
|
1634
|
+
console.log(clr(c.green, ' ✓'));
|
|
1635
|
+
console.log(clr(c.dim, ` Key saved to ~/.commit/config. Backup sent to ${emailArg}.`));
|
|
1636
|
+
} else {
|
|
1637
|
+
const errMsg = keyData.error === 'rate_limit_exceeded'
|
|
1638
|
+
? 'Too many keys from this IP today.'
|
|
1639
|
+
: (keyData.message || 'Could not create key.');
|
|
1640
|
+
console.error(clr(c.red, ` ${errMsg}`));
|
|
1641
|
+
process.exit(1);
|
|
1642
|
+
}
|
|
1643
|
+
} catch (err) {
|
|
1644
|
+
console.error(clr(c.red, ` Error: ${err.message}`));
|
|
1645
|
+
process.exit(1);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1560
1649
|
if (!key) {
|
|
1561
|
-
console.error(clr(c.red, 'No API key found.
|
|
1562
|
-
console.error(
|
|
1650
|
+
console.error(clr(c.red, 'No API key found.'));
|
|
1651
|
+
console.error('');
|
|
1652
|
+
console.error(clr(c.bold, ' One-step setup — creates key + starts monitoring:'));
|
|
1653
|
+
console.error(clr(c.cyan, ` poc watch ${pkg} --email you@company.com`));
|
|
1654
|
+
console.error('');
|
|
1655
|
+
console.error(clr(c.dim, ' Or set COMMIT_API_KEY / add api_key=<key> to ~/.commit/config'));
|
|
1563
1656
|
process.exit(1);
|
|
1564
1657
|
}
|
|
1565
1658
|
|
|
1566
|
-
process.stdout.write(clr(c.dim, `Adding ${pkg} (${ecosystem}) to watchlist...`));
|
|
1659
|
+
process.stdout.write(clr(c.dim, ` Adding ${pkg} (${ecosystem}) to watchlist...`));
|
|
1567
1660
|
const res = await fetch(WATCHLIST_API, {
|
|
1568
1661
|
method: 'POST',
|
|
1569
1662
|
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${key}` },
|
|
@@ -1571,6 +1664,16 @@ async function cmdWatch(pkg, ecosystem) {
|
|
|
1571
1664
|
});
|
|
1572
1665
|
|
|
1573
1666
|
if (res.status === 402) { process.stdout.write('\n'); await printUpgradeRequired(res, 'watch-cmd'); process.exit(1); }
|
|
1667
|
+
if (res.status === 422) {
|
|
1668
|
+
const errData = await res.json().catch(() => ({}));
|
|
1669
|
+
process.stdout.write('\n');
|
|
1670
|
+
console.log(clr(c.yellow, ` ⚠ ${errData.message || 'Package limit reached.'}`));
|
|
1671
|
+
if (errData.upgrade) {
|
|
1672
|
+
console.log(clr(c.dim, ` ${errData.upgrade.message || `Upgrade to ${errData.upgrade.plan} for more:`}`));
|
|
1673
|
+
console.log(clr(c.cyan, ` ${errData.upgrade.url}`));
|
|
1674
|
+
}
|
|
1675
|
+
process.exit(1);
|
|
1676
|
+
}
|
|
1574
1677
|
|
|
1575
1678
|
const data = await res.json();
|
|
1576
1679
|
if (!res.ok) {
|
|
@@ -1582,7 +1685,7 @@ async function cmdWatch(pkg, ecosystem) {
|
|
|
1582
1685
|
process.stdout.write('\n');
|
|
1583
1686
|
if (isNew) {
|
|
1584
1687
|
console.log(clr(c.green, ` ✓ Now watching ${pkg}`));
|
|
1585
|
-
console.log(clr(c.dim, '
|
|
1688
|
+
console.log(clr(c.dim, ' Weekly digest (Mondays). Upgrade to Developer ($15/mo) for daily scans + Slack alerts.'));
|
|
1586
1689
|
} else {
|
|
1587
1690
|
console.log(clr(c.dim, ` ↩ ${pkg} is already in your watchlist`));
|
|
1588
1691
|
}
|
|
@@ -2177,15 +2280,17 @@ async function main() {
|
|
|
2177
2280
|
|
|
2178
2281
|
if (subcmd === 'watch') {
|
|
2179
2282
|
const pkg = args[1];
|
|
2180
|
-
if (!pkg) { console.error('Usage: poc watch <package> [--ecosystem npm|pypi|cargo|golang]'); process.exit(1); }
|
|
2283
|
+
if (!pkg) { console.error('Usage: poc watch <package> [--email you@co.com] [--ecosystem npm|pypi|cargo|golang]'); process.exit(1); }
|
|
2181
2284
|
let ecosystem = 'npm';
|
|
2285
|
+
let email = null;
|
|
2182
2286
|
for (let i = 2; i < args.length; i++) {
|
|
2183
2287
|
if (args[i] === '--ecosystem' || args[i] === '-e') ecosystem = args[++i] || 'npm';
|
|
2288
|
+
else if (args[i] === '--email') email = args[++i] || null;
|
|
2184
2289
|
else if (args[i] === '--pypi') ecosystem = 'pypi';
|
|
2185
2290
|
else if (args[i] === '--cargo') ecosystem = 'cargo';
|
|
2186
2291
|
else if (args[i] === '--golang' || args[i] === '--go') ecosystem = 'golang';
|
|
2187
2292
|
}
|
|
2188
|
-
await cmdWatch(pkg, ecosystem);
|
|
2293
|
+
await cmdWatch(pkg, ecosystem, email);
|
|
2189
2294
|
process.exit(0);
|
|
2190
2295
|
}
|
|
2191
2296
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "proof-of-commitment",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.1",
|
|
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",
|
|
@@ -34,7 +34,15 @@
|
|
|
34
34
|
"maintainer",
|
|
35
35
|
"publisher",
|
|
36
36
|
"provenance",
|
|
37
|
-
"trusted-publishing"
|
|
37
|
+
"trusted-publishing",
|
|
38
|
+
"mcp",
|
|
39
|
+
"mcp-server",
|
|
40
|
+
"vulnerability",
|
|
41
|
+
"sca",
|
|
42
|
+
"dependency-audit",
|
|
43
|
+
"lockfile",
|
|
44
|
+
"devsecops",
|
|
45
|
+
"ci"
|
|
38
46
|
],
|
|
39
47
|
"author": "piiiico",
|
|
40
48
|
"license": "MIT",
|