proof-of-commitment 1.20.0 → 1.21.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.
- package/README.md +12 -15
- package/index.js +229 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,26 +51,23 @@ npx proof-of-commitment --file go.sum # full transitive set
|
|
|
51
51
|
|
|
52
52
|
**Web demo (no install):** [getcommit.dev/audit](https://getcommit.dev/audit) — paste your packages, see risk scores in seconds.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
## Get notified before the next attack
|
|
55
|
+
|
|
56
|
+
The CLI tells you what's risky today. A free API key unlocks **monitoring** — daily score recomputation across the packages you depend on, with alerts when one degrades (publisher drops, release stalls, score falls ≥10 points).
|
|
57
|
+
|
|
58
|
+
[**Get a free API key →**](https://getcommit.dev/get-started?ref=npm-readme-monitoring&utm_source=cli) — no card, 30 seconds · 200 audits/day free · Developer $15/mo unlocks alerts + watchlist.
|
|
59
|
+
|
|
55
60
|
```bash
|
|
56
61
|
# Install once, then use the `poc` alias:
|
|
57
62
|
npm install -g proof-of-commitment
|
|
58
63
|
|
|
59
|
-
#
|
|
60
|
-
poc login sk_commit_your_key_here
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
poc status # check tier + usage anytime
|
|
64
|
-
poc logout # remove saved key
|
|
65
|
-
|
|
66
|
-
# Monitoring (Developer $15/mo+ — daily scans + alerts):
|
|
67
|
-
poc watch chalk
|
|
64
|
+
# After getting your free key:
|
|
65
|
+
poc login sk_commit_your_key_here # save and validate
|
|
66
|
+
poc status # check tier + usage
|
|
67
|
+
poc watch chalk # start monitoring (Developer $15/mo)
|
|
68
68
|
poc watch requests --ecosystem pypi
|
|
69
|
-
poc
|
|
70
|
-
poc
|
|
71
|
-
poc unwatch chalk
|
|
72
|
-
|
|
73
|
-
# Enable monitoring: https://getcommit.dev/pricing
|
|
69
|
+
poc watchlist # view all watched packages
|
|
70
|
+
poc init # add CI gate to this project
|
|
74
71
|
```
|
|
75
72
|
|
|
76
73
|
Alerts fire on: score drop ≥10 points · package crosses CRITICAL threshold · recovery to HEALTHY.
|
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.21.0
|
|
4
4
|
* Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
|
|
5
5
|
* Usage: npx proof-of-commitment [packages...] [options]
|
|
6
6
|
*/
|
|
@@ -22,6 +22,22 @@ const JSON_API_HEADERS = {
|
|
|
22
22
|
'Accept': 'application/json',
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Build /api/audit request headers, adding Authorization: Bearer <key>
|
|
27
|
+
* when a key is present in COMMIT_API_KEY or ~/.commit/config.
|
|
28
|
+
*
|
|
29
|
+
* Without this, signed-up users hitting 429 stayed stuck: the inline-signup
|
|
30
|
+
* (v1.20.0) and URL signup flows both save the key locally, but the audit
|
|
31
|
+
* call site never read it — so "Re-run your command" still 429'd. Fixed
|
|
32
|
+
* in v1.20.1 after live dogfood confirmed the dead-end (see commit log).
|
|
33
|
+
*/
|
|
34
|
+
async function auditHeaders() {
|
|
35
|
+
const key = await readApiKey();
|
|
36
|
+
return key
|
|
37
|
+
? { ...JSON_API_HEADERS, Authorization: `Bearer ${key}` }
|
|
38
|
+
: JSON_API_HEADERS;
|
|
39
|
+
}
|
|
40
|
+
|
|
25
41
|
// ANSI color helpers
|
|
26
42
|
const c = {
|
|
27
43
|
reset: '\x1b[0m',
|
|
@@ -332,6 +348,7 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
|
|
|
332
348
|
console.log(clr(c.cyan, `\n 🔗 Full report: ${WEB}?packages=${encodeURIComponent(topPkgs)}&${utm}`));
|
|
333
349
|
console.log(clr(c.cyan, ` 🤖 GitHub Action: github.com/piiiico/commit-action — block CRITICAL packages in CI`));
|
|
334
350
|
console.log(clr(c.dim, ` 📋 Add to this project: `) + clr(c.cyan, `poc init`) + clr(c.dim, ` — creates workflow + README badge`));
|
|
351
|
+
console.log(clr(c.dim, ` 🛡️ Protect every install: `) + clr(c.cyan, `poc hook`) + clr(c.dim, ` — Cursor hook, blocks CRITICAL before npm/pip/cargo runs`));
|
|
335
352
|
|
|
336
353
|
// Per-package profile URLs — drive traffic to permanent, indexable pages
|
|
337
354
|
const ecoPath = { npm: 'npm', pypi: 'pypi', cargo: 'cargo', golang: 'go' };
|
|
@@ -462,7 +479,7 @@ async function inlineSignup(results) {
|
|
|
462
479
|
|
|
463
480
|
function printHelp() {
|
|
464
481
|
console.log(`
|
|
465
|
-
${clr(c.bold, 'proof-of-commitment')} v1.
|
|
482
|
+
${clr(c.bold, 'proof-of-commitment')} v1.21.0 — supply chain risk scorer
|
|
466
483
|
|
|
467
484
|
${clr(c.bold, 'Usage:')}
|
|
468
485
|
npx proof-of-commitment Auto-detect manifest in current dir
|
|
@@ -488,6 +505,11 @@ ${clr(c.bold, 'Reports:')}
|
|
|
488
505
|
poc report [pkgs] Same flags as scan — packages, --pypi, --cargo, --file, etc.
|
|
489
506
|
Saves audit-report.html to cwd + prints Markdown for GitHub issues
|
|
490
507
|
|
|
508
|
+
${clr(c.bold, 'IDE Hooks:')}
|
|
509
|
+
poc hook Install Cursor beforeShellExecution hook (blocks CRITICAL packages)
|
|
510
|
+
poc hook --global Install for all Cursor projects (~/.cursor/hooks.json)
|
|
511
|
+
poc hook --uninstall Remove the hook
|
|
512
|
+
|
|
491
513
|
${clr(c.bold, 'Account:')}
|
|
492
514
|
poc login [key] Save and validate your API key (interactive or direct)
|
|
493
515
|
poc status Show current tier, usage, and limits
|
|
@@ -876,11 +898,13 @@ async function auditBatched(packages, ecosystem, { onProgress } = {}) {
|
|
|
876
898
|
|
|
877
899
|
let completed = 0;
|
|
878
900
|
let batchedCta = null;
|
|
901
|
+
// Resolve auth once so all parallel batches share the same key lookup.
|
|
902
|
+
const headers = await auditHeaders();
|
|
879
903
|
const results = await Promise.all(
|
|
880
904
|
batches.map(async (batch) => {
|
|
881
905
|
const res = await fetch(API, {
|
|
882
906
|
method: 'POST',
|
|
883
|
-
headers
|
|
907
|
+
headers,
|
|
884
908
|
body: JSON.stringify({ packages: batch, ecosystem }),
|
|
885
909
|
});
|
|
886
910
|
if (!res.ok) {
|
|
@@ -1107,6 +1131,201 @@ async function cmdLogout() {
|
|
|
1107
1131
|
console.log();
|
|
1108
1132
|
}
|
|
1109
1133
|
|
|
1134
|
+
/**
|
|
1135
|
+
* poc hook [--cursor] [--global] [--uninstall]
|
|
1136
|
+
* Install a beforeShellExecution hook for Cursor that scores packages before install.
|
|
1137
|
+
* Writes a standalone Node.js hook script to ~/.commit/cursor-hook.js and
|
|
1138
|
+
* configures .cursor/hooks.json (project) or ~/.cursor/hooks.json (--global).
|
|
1139
|
+
*/
|
|
1140
|
+
async function cmdHook(args) {
|
|
1141
|
+
const os = await import('os');
|
|
1142
|
+
const fs = await import('fs');
|
|
1143
|
+
const path = await import('path');
|
|
1144
|
+
|
|
1145
|
+
const isGlobal = args.includes('--global') || args.includes('-g');
|
|
1146
|
+
const uninstall = args.includes('--uninstall') || args.includes('--remove');
|
|
1147
|
+
|
|
1148
|
+
// ── Hook script (plain Node.js, no external deps) ─────────────────────
|
|
1149
|
+
const hookScript = `#!/usr/bin/env node
|
|
1150
|
+
/**
|
|
1151
|
+
* Commit supply chain hook for Cursor (auto-generated by \`poc hook\`)
|
|
1152
|
+
* Intercepts npm/pip/cargo/go install commands and scores packages
|
|
1153
|
+
* against getcommit.dev before they run.
|
|
1154
|
+
*
|
|
1155
|
+
* CRITICAL packages are blocked. HIGH packages trigger confirmation.
|
|
1156
|
+
* Docs: https://getcommit.dev/docs/cursor-hook
|
|
1157
|
+
*/
|
|
1158
|
+
const API = 'https://poc-backend.amdal-dev.workers.dev/api/audit';
|
|
1159
|
+
const fs = require('fs');
|
|
1160
|
+
const path = require('path');
|
|
1161
|
+
|
|
1162
|
+
function readKey() {
|
|
1163
|
+
try {
|
|
1164
|
+
if (process.env.COMMIT_API_KEY) return process.env.COMMIT_API_KEY.trim();
|
|
1165
|
+
const cfg = fs.readFileSync(path.join(require('os').homedir(), '.commit', 'config'), 'utf-8');
|
|
1166
|
+
const m = cfg.match(/^api_key\\s*=\\s*(.+)$/m);
|
|
1167
|
+
return m ? m[1].trim() : '';
|
|
1168
|
+
} catch { return ''; }
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function parseInstall(cmd) {
|
|
1172
|
+
const t = cmd.trim();
|
|
1173
|
+
let m;
|
|
1174
|
+
// npm / pnpm / yarn
|
|
1175
|
+
m = t.match(/^(?:npm\\s+(?:i|install|add)|pnpm\\s+(?:i|install|add)|yarn\\s+add)\\s+(.+)/);
|
|
1176
|
+
if (m) return { eco: 'npm', pkgs: m[1].split(/\\s+/).filter(a => !a.startsWith('-') && a !== 'install' && a !== 'add') };
|
|
1177
|
+
// pip
|
|
1178
|
+
m = t.match(/^(?:pip3?\\s+install|uv\\s+pip\\s+install|python3?\\s+-m\\s+pip\\s+install)\\s+(.+)/);
|
|
1179
|
+
if (m) return { eco: 'pypi', pkgs: m[1].split(/\\s+/).filter(a => !a.startsWith('-')).map(a => a.split('==')[0].split('>=')[0]) };
|
|
1180
|
+
// cargo
|
|
1181
|
+
m = t.match(/^cargo\\s+(?:add|install)\\s+(.+)/);
|
|
1182
|
+
if (m) return { eco: 'cargo', pkgs: m[1].split(/\\s+/).filter(a => !a.startsWith('-')) };
|
|
1183
|
+
// go
|
|
1184
|
+
m = t.match(/^go\\s+(?:get|install)\\s+(.+)/);
|
|
1185
|
+
if (m) return { eco: 'golang', pkgs: m[1].split(/\\s+/).filter(a => !a.startsWith('-')).map(a => a.split('@')[0]) };
|
|
1186
|
+
return null;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
async function main() {
|
|
1190
|
+
let input;
|
|
1191
|
+
try { input = JSON.parse(fs.readFileSync('/dev/stdin', 'utf-8')); } catch { process.stdout.write(JSON.stringify({ permission: 'allow' })); return; }
|
|
1192
|
+
const parsed = parseInstall(input.command || '');
|
|
1193
|
+
if (!parsed || parsed.pkgs.length === 0) { process.stdout.write(JSON.stringify({ permission: 'allow' })); return; }
|
|
1194
|
+
|
|
1195
|
+
const headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' };
|
|
1196
|
+
const key = readKey();
|
|
1197
|
+
if (key) headers['Authorization'] = 'Bearer ' + key;
|
|
1198
|
+
|
|
1199
|
+
try {
|
|
1200
|
+
const ctrl = new AbortController();
|
|
1201
|
+
const timer = setTimeout(() => ctrl.abort(), 4000);
|
|
1202
|
+
const res = await fetch(API, { method: 'POST', headers, body: JSON.stringify({ packages: parsed.pkgs, ecosystem: parsed.eco }), signal: ctrl.signal });
|
|
1203
|
+
clearTimeout(timer);
|
|
1204
|
+
if (!res.ok && res.status !== 429) { process.stdout.write(JSON.stringify({ permission: 'allow' })); return; }
|
|
1205
|
+
const data = await res.json();
|
|
1206
|
+
const results = data.results || data.packages_already_scored || [];
|
|
1207
|
+
|
|
1208
|
+
const critical = results.filter(r => (r.riskFlags || []).some(f => f.startsWith('CRITICAL')));
|
|
1209
|
+
const high = results.filter(r => (r.riskFlags || []).some(f => f.startsWith('HIGH')));
|
|
1210
|
+
const url = 'https://getcommit.dev/audit?packages=' + parsed.pkgs.join(',') + '&ecosystem=' + parsed.eco;
|
|
1211
|
+
|
|
1212
|
+
if (critical.length > 0) {
|
|
1213
|
+
const lines = critical.map(r => ' \\u{1F534} ' + r.name + ' (score ' + (r.score||'?') + ') \\u2014 ' + (r.riskFlags||[]).slice(0,1).join(', '));
|
|
1214
|
+
process.stdout.write(JSON.stringify({
|
|
1215
|
+
permission: 'deny',
|
|
1216
|
+
user_message: '\\u{1F534} Commit blocked: ' + critical.map(r=>r.name).join(', ') + ' flagged CRITICAL\\n\\n' + lines.join('\\n') + '\\n\\n\\u2192 ' + url,
|
|
1217
|
+
agent_message: 'Package install blocked by Commit. CRITICAL = sole publisher + high downloads (attack surface of axios/node-ipc incidents). ' + critical.map(r=>r.name).join(', ') + '. Report: ' + url,
|
|
1218
|
+
}));
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (high.length > 0) {
|
|
1222
|
+
const lines = high.map(r => ' \\u{1F7E1} ' + r.name + ' (score ' + (r.score||'?') + ') \\u2014 ' + (r.riskFlags||[]).slice(0,1).join(', '));
|
|
1223
|
+
process.stdout.write(JSON.stringify({
|
|
1224
|
+
permission: 'ask',
|
|
1225
|
+
user_message: '\\u{1F7E1} Commit: ' + high.map(r=>r.name).join(', ') + ' scored HIGH risk\\n\\n' + lines.join('\\n') + '\\n\\nProceed? \\u2192 ' + url,
|
|
1226
|
+
}));
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
process.stdout.write(JSON.stringify({ permission: 'allow' }));
|
|
1230
|
+
} catch { process.stdout.write(JSON.stringify({ permission: 'allow' })); }
|
|
1231
|
+
}
|
|
1232
|
+
main();
|
|
1233
|
+
`;
|
|
1234
|
+
|
|
1235
|
+
const commitDir = path.join(os.homedir(), '.commit');
|
|
1236
|
+
const hookPath = path.join(commitDir, 'cursor-hook.js');
|
|
1237
|
+
|
|
1238
|
+
// ── Uninstall ──────────────────────────────────────────────────────────
|
|
1239
|
+
if (uninstall) {
|
|
1240
|
+
let removed = false;
|
|
1241
|
+
if (fs.existsSync(hookPath)) {
|
|
1242
|
+
fs.unlinkSync(hookPath);
|
|
1243
|
+
removed = true;
|
|
1244
|
+
}
|
|
1245
|
+
// Remove from hooks.json
|
|
1246
|
+
for (const hooksDir of [
|
|
1247
|
+
path.join(process.cwd(), '.cursor'),
|
|
1248
|
+
path.join(os.homedir(), '.cursor'),
|
|
1249
|
+
]) {
|
|
1250
|
+
const hooksFile = path.join(hooksDir, 'hooks.json');
|
|
1251
|
+
if (fs.existsSync(hooksFile)) {
|
|
1252
|
+
try {
|
|
1253
|
+
const cfg = JSON.parse(fs.readFileSync(hooksFile, 'utf-8'));
|
|
1254
|
+
const hooks = cfg.hooks?.beforeShellExecution || [];
|
|
1255
|
+
const filtered = hooks.filter(h => !h.command?.includes('cursor-hook.js'));
|
|
1256
|
+
if (filtered.length !== hooks.length) {
|
|
1257
|
+
cfg.hooks.beforeShellExecution = filtered;
|
|
1258
|
+
fs.writeFileSync(hooksFile, JSON.stringify(cfg, null, 2) + '\n');
|
|
1259
|
+
removed = true;
|
|
1260
|
+
console.log(clr(c.dim, ` Updated: ${hooksFile}`));
|
|
1261
|
+
}
|
|
1262
|
+
} catch {}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
if (removed) {
|
|
1266
|
+
console.log(clr(c.green, '\n ✓ Cursor hook uninstalled.'));
|
|
1267
|
+
} else {
|
|
1268
|
+
console.log(clr(c.dim, '\n No hook found to remove.'));
|
|
1269
|
+
}
|
|
1270
|
+
console.log();
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// ── Install ────────────────────────────────────────────────────────────
|
|
1275
|
+
// 1. Write hook script
|
|
1276
|
+
if (!fs.existsSync(commitDir)) fs.mkdirSync(commitDir, { recursive: true });
|
|
1277
|
+
fs.writeFileSync(hookPath, hookScript, { mode: 0o755 });
|
|
1278
|
+
|
|
1279
|
+
// 2. Configure hooks.json
|
|
1280
|
+
const hooksDir = isGlobal
|
|
1281
|
+
? path.join(os.homedir(), '.cursor')
|
|
1282
|
+
: path.join(process.cwd(), '.cursor');
|
|
1283
|
+
if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
|
|
1284
|
+
|
|
1285
|
+
const hooksFile = path.join(hooksDir, 'hooks.json');
|
|
1286
|
+
let cfg = { version: 1, hooks: {} };
|
|
1287
|
+
if (fs.existsSync(hooksFile)) {
|
|
1288
|
+
try { cfg = JSON.parse(fs.readFileSync(hooksFile, 'utf-8')); } catch {}
|
|
1289
|
+
}
|
|
1290
|
+
if (!cfg.hooks) cfg.hooks = {};
|
|
1291
|
+
if (!cfg.hooks.beforeShellExecution) cfg.hooks.beforeShellExecution = [];
|
|
1292
|
+
|
|
1293
|
+
// Avoid duplicates
|
|
1294
|
+
const existing = cfg.hooks.beforeShellExecution.some(h => h.command?.includes('cursor-hook.js'));
|
|
1295
|
+
if (!existing) {
|
|
1296
|
+
cfg.hooks.beforeShellExecution.push({
|
|
1297
|
+
command: `node ${hookPath}`,
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
fs.writeFileSync(hooksFile, JSON.stringify(cfg, null, 2) + '\n');
|
|
1301
|
+
|
|
1302
|
+
// 3. Report
|
|
1303
|
+
console.log(clr(c.green, '\n ✓ Cursor supply chain hook installed'));
|
|
1304
|
+
console.log();
|
|
1305
|
+
console.log(clr(c.bold, ' What happens now:'));
|
|
1306
|
+
console.log(clr(c.dim, ' Every ') + clr(c.cyan, 'npm install') + clr(c.dim, ', ') +
|
|
1307
|
+
clr(c.cyan, 'pip install') + clr(c.dim, ', ') + clr(c.cyan, 'cargo add') + clr(c.dim, ', and ') +
|
|
1308
|
+
clr(c.cyan, 'go get') + clr(c.dim, ' in Cursor'));
|
|
1309
|
+
console.log(clr(c.dim, ' is scored against Commit before it runs.'));
|
|
1310
|
+
console.log(clr(c.dim, ' CRITICAL packages are blocked. HIGH packages ask for confirmation.'));
|
|
1311
|
+
console.log();
|
|
1312
|
+
console.log(clr(c.bold, ' Files:'));
|
|
1313
|
+
console.log(clr(c.dim, ` Hook script: ${hookPath}`));
|
|
1314
|
+
console.log(clr(c.dim, ` Config: ${hooksFile}`));
|
|
1315
|
+
console.log();
|
|
1316
|
+
|
|
1317
|
+
const key = await readApiKey();
|
|
1318
|
+
if (!key) {
|
|
1319
|
+
console.log(clr(c.yellow, ' ⚠ No API key found.') + clr(c.dim, ' Anonymous limit: 15 audits/day.'));
|
|
1320
|
+
console.log(clr(c.dim, ' Get a free key (200/day): ') + clr(c.cyan, 'poc login'));
|
|
1321
|
+
console.log(clr(c.dim, ' Or: ') + clr(c.cyan, 'https://getcommit.dev/get-started?ref=cursor-hook&utm_source=cli'));
|
|
1322
|
+
console.log();
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
console.log(clr(c.dim, ' Uninstall: ') + clr(c.cyan, 'poc hook --uninstall'));
|
|
1326
|
+
console.log();
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1110
1329
|
function tierLabel(tier) {
|
|
1111
1330
|
if (tier === 'pro') return clr(c.cyan + c.bold, 'Pro');
|
|
1112
1331
|
if (tier === 'enterprise') return clr(c.magenta + c.bold, 'Enterprise');
|
|
@@ -1479,7 +1698,7 @@ async function cmdReport(packages, ecosystem, { filePath, isLockfile, totalScann
|
|
|
1479
1698
|
if (packages.length <= 20) {
|
|
1480
1699
|
const res = await fetch(API, {
|
|
1481
1700
|
method: 'POST',
|
|
1482
|
-
headers:
|
|
1701
|
+
headers: await auditHeaders(),
|
|
1483
1702
|
body: JSON.stringify({ packages, ecosystem }),
|
|
1484
1703
|
});
|
|
1485
1704
|
if (!res.ok) {
|
|
@@ -1705,6 +1924,11 @@ async function main() {
|
|
|
1705
1924
|
process.exit(0);
|
|
1706
1925
|
}
|
|
1707
1926
|
|
|
1927
|
+
if (subcmd === 'hook') {
|
|
1928
|
+
await cmdHook(args.slice(1));
|
|
1929
|
+
process.exit(0);
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1708
1932
|
if (subcmd === 'report') {
|
|
1709
1933
|
// Parse report args (same flags as main scan)
|
|
1710
1934
|
const reportArgs = args.slice(1);
|
|
@@ -1881,7 +2105,7 @@ async function main() {
|
|
|
1881
2105
|
try {
|
|
1882
2106
|
const res = await fetch(API, {
|
|
1883
2107
|
method: 'POST',
|
|
1884
|
-
headers:
|
|
2108
|
+
headers: await auditHeaders(),
|
|
1885
2109
|
body: JSON.stringify({ packages, ecosystem }),
|
|
1886
2110
|
});
|
|
1887
2111
|
if (!res.ok) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "proof-of-commitment",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"mcpName": "io.github.piiiico/proof-of-commitment",
|
|
5
5
|
"description": "Supply chain risk scorer for npm, PyPI, Cargo, and Go packages — behavioral signals that can't be faked",
|
|
6
6
|
"type": "module",
|