proof-of-commitment 1.11.0 → 1.12.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.
Files changed (2) hide show
  1. package/index.js +163 -7
  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.11.0
3
+ * proof-of-commitment CLI v1.12.1
4
4
  * Scores npm/PyPI/Cargo/Go packages on behavioral commitment signals.
5
5
  * Usage: npx proof-of-commitment [packages...] [options]
6
6
  */
@@ -180,8 +180,22 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
180
180
 
181
181
  // Footer with web link + CI integration CTA
182
182
  const topPkgs = results.slice(0, 10).map(r => r.name).join(',');
183
- console.log(clr(c.cyan, `\n 🔗 Full report: ${WEB}?packages=${encodeURIComponent(topPkgs)}`));
183
+ const utm = 'utm_source=cli&utm_medium=audit';
184
+ console.log(clr(c.cyan, `\n 🔗 Full report: ${WEB}?packages=${encodeURIComponent(topPkgs)}&${utm}`));
184
185
  console.log(clr(c.cyan, ` 🤖 GitHub Action: github.com/piiiico/commit-action — block CRITICAL packages in CI`));
186
+ console.log(clr(c.dim, ` 📋 Add to this project: `) + clr(c.cyan, `poc init`) + clr(c.dim, ` — creates workflow + README badge`));
187
+
188
+ // Per-package profile URLs — drive traffic to permanent, indexable pages
189
+ const ecoPath = { npm: 'npm', pypi: 'pypi', cargo: 'cargo', golang: 'go' };
190
+ const profilePkgs = results.slice(0, 5).filter(r => r.name && ecoPath[r.ecosystem || 'npm']);
191
+ if (profilePkgs.length > 0) {
192
+ console.log(clr(c.dim, `\n 📄 Package profiles:`));
193
+ for (const r of profilePkgs) {
194
+ const eco = ecoPath[r.ecosystem || 'npm'];
195
+ const name = encodeURIComponent(r.name).replace(/%40/g, '@').replace(/%2F/g, '/');
196
+ console.log(clr(c.dim, ` getcommit.dev/${eco}/${name}`));
197
+ }
198
+ }
185
199
 
186
200
  // Contextual upsell — show when findings make monitoring relevant
187
201
  if (effectiveCritical > 0) {
@@ -192,7 +206,7 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
192
206
  clr(c.cyan, `poc watch ${results.find(r => hasCritical(r.riskFlags))?.name || results[0]?.name}`));
193
207
  } else {
194
208
  console.log(clr(c.dim, `\n 📊 Monitor ${effectiveCritical === 1 ? 'this' : 'these ' + effectiveCritical} CRITICAL ${effectiveCritical === 1 ? 'package' : 'packages'} — get alerted when scores change.`));
195
- console.log(clr(c.dim, ' Get a free API key: ') + clr(c.cyan, 'https://getcommit.dev/get-started'));
209
+ console.log(clr(c.dim, ' Get a free API key: ') + clr(c.cyan, 'https://getcommit.dev/get-started?utm_source=cli'));
196
210
  console.log(clr(c.dim, ' Then run: ') + clr(c.cyan, 'poc login'));
197
211
  }
198
212
  }
@@ -201,7 +215,7 @@ function printTable(results, { totalScanned, totalCritical, lockfile } = {}) {
201
215
 
202
216
  function printHelp() {
203
217
  console.log(`
204
- ${clr(c.bold, 'proof-of-commitment')} v1.11.0 — supply chain risk scorer
218
+ ${clr(c.bold, 'proof-of-commitment')} v1.12.1 — supply chain risk scorer
205
219
 
206
220
  ${clr(c.bold, 'Usage:')}
207
221
  npx proof-of-commitment Auto-detect manifest in current dir
@@ -218,6 +232,10 @@ ${clr(c.bold, 'Usage:')}
218
232
  npx proof-of-commitment --file go.mod Audit Go direct + indirect deps
219
233
  npx proof-of-commitment --file go.sum Audit Go full transitive set
220
234
 
235
+ ${clr(c.bold, 'Setup:')}
236
+ poc init Add a GitHub Action + README badge to the current project
237
+ Auto-detects ecosystem. Blocks CRITICAL packages on every PR.
238
+
221
239
  ${clr(c.bold, 'Reports:')}
222
240
  poc report Scan and generate a shareable HTML report + Markdown snippet
223
241
  poc report [pkgs] Same flags as scan — packages, --pypi, --cargo, --file, etc.
@@ -780,7 +798,7 @@ async function cmdLogin(keyArg) {
780
798
  console.log(clr(c.dim, ' poc watchlist View monitored packages'));
781
799
  console.log(clr(c.dim, ' poc unwatch <package> Remove from monitoring'));
782
800
  } else {
783
- console.log(clr(c.dim, ' Upgrade to Pro for monitoring + alerts: https://getcommit.dev/pricing'));
801
+ console.log(clr(c.dim, ' Upgrade to Pro for monitoring + alerts: https://getcommit.dev/pricing?utm_source=cli'));
784
802
  }
785
803
  console.log();
786
804
  }
@@ -859,7 +877,7 @@ async function cmdWatch(pkg, ecosystem) {
859
877
  const key = await readApiKey();
860
878
  if (!key) {
861
879
  console.error(clr(c.red, 'No API key found. Set COMMIT_API_KEY or add api_key=<key> to ~/.commit/config'));
862
- console.error(clr(c.dim, 'Get a key at https://getcommit.dev/pricing'));
880
+ console.error(clr(c.dim, 'Get a key at https://getcommit.dev/pricing?utm_source=cli'));
863
881
  process.exit(1);
864
882
  }
865
883
 
@@ -1124,7 +1142,7 @@ ${rows}
1124
1142
  <div class="footer">
1125
1143
  <span>Generated by <a href="${WEB}" target="_blank">proof-of-commitment</a></span>
1126
1144
  <span><a href="https://github.com/piiiico/commit-action" target="_blank">GitHub Action</a></span>
1127
- <span><a href="https://getcommit.dev/pricing" target="_blank">Commit Pro</a></span>
1145
+ <span><a href="https://getcommit.dev/pricing?utm_source=cli&amp;utm_medium=report" target="_blank">Commit Pro</a></span>
1128
1146
  </div>
1129
1147
  <script>
1130
1148
  function copyMd() {
@@ -1251,6 +1269,139 @@ async function cmdReport(packages, ecosystem, { filePath, isLockfile, totalScann
1251
1269
  console.log();
1252
1270
  }
1253
1271
 
1272
+ /**
1273
+ * poc init — scaffold a GitHub Action workflow + README badge for the current project.
1274
+ * Turns every CLI user into a permanent distribution node.
1275
+ */
1276
+ async function cmdInit() {
1277
+ const fs = await import('fs');
1278
+ const path = await import('path');
1279
+ const cwd = process.cwd();
1280
+
1281
+ // Detect ecosystem from project files
1282
+ let ecosystem = 'npm';
1283
+ let manifestName = 'package.json';
1284
+ const checks = [
1285
+ ['package.json', 'npm'],
1286
+ ['package-lock.json', 'npm'],
1287
+ ['yarn.lock', 'npm'],
1288
+ ['pnpm-lock.yaml', 'npm'],
1289
+ ['requirements.txt', 'pypi'],
1290
+ ['Cargo.toml', 'cargo'],
1291
+ ['go.mod', 'golang'],
1292
+ ];
1293
+ for (const [file, eco] of checks) {
1294
+ if (fs.existsSync(path.join(cwd, file))) {
1295
+ ecosystem = eco;
1296
+ manifestName = file;
1297
+ break;
1298
+ }
1299
+ }
1300
+
1301
+ console.log(clr(c.bold, '\n Commit — supply chain audit for CI\n'));
1302
+ console.log(clr(c.dim, ` Detected: ${manifestName} (${ecosystem})`));
1303
+
1304
+ // ── 1. GitHub Action workflow ──
1305
+ const workflowDir = path.join(cwd, '.github', 'workflows');
1306
+ const workflowPath = path.join(workflowDir, 'commit-audit.yml');
1307
+
1308
+ const ecoFlag = ecosystem === 'npm' ? '' : ` --${ecosystem === 'golang' ? 'go' : ecosystem}`;
1309
+ const workflowContent = `# Supply chain audit — powered by Commit (getcommit.dev)
1310
+ # Scores dependencies on behavioral signals: publisher concentration,
1311
+ # download anomalies, release patterns, trusted publishing adoption.
1312
+ # Blocks PRs when CRITICAL packages are found (configurable).
1313
+
1314
+ name: Supply Chain Audit
1315
+
1316
+ on:
1317
+ pull_request:
1318
+ push:
1319
+ branches: [main, master]
1320
+ schedule:
1321
+ - cron: '0 9 * * 1' # Weekly Monday 09:00 UTC
1322
+
1323
+ jobs:
1324
+ audit:
1325
+ runs-on: ubuntu-latest
1326
+ steps:
1327
+ - uses: actions/checkout@v4
1328
+ - uses: actions/setup-node@v4
1329
+ with:
1330
+ node-version: '20'
1331
+ - run: npx -y proof-of-commitment${ecoFlag} --fail-on=critical
1332
+ `;
1333
+
1334
+ let workflowCreated = false;
1335
+ if (fs.existsSync(workflowPath)) {
1336
+ console.log(clr(c.yellow, ` ⚠ .github/workflows/commit-audit.yml already exists — skipped`));
1337
+ } else {
1338
+ fs.mkdirSync(workflowDir, { recursive: true });
1339
+ fs.writeFileSync(workflowPath, workflowContent);
1340
+ workflowCreated = true;
1341
+ console.log(clr(c.green, ` ✓ Created .github/workflows/commit-audit.yml`));
1342
+ }
1343
+
1344
+ // ── 2. README badge ──
1345
+ // Try to read project name from package.json or directory name
1346
+ let projectName = path.basename(cwd);
1347
+ try {
1348
+ const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8'));
1349
+ if (pkg.name) projectName = pkg.name;
1350
+ } catch {}
1351
+
1352
+ const badgeUrl = `https://poc-backend.amdal-dev.workers.dev/badge/npm/${encodeURIComponent(projectName)}`;
1353
+ const auditUrl = `https://getcommit.dev/audit?packages=${encodeURIComponent(projectName)}`;
1354
+ const badgeMd = `[![Commit Score](${badgeUrl})](${auditUrl})`;
1355
+
1356
+ console.log(clr(c.green, ` ✓ README badge (paste into your README.md):\n`));
1357
+ console.log(` ${badgeMd}\n`);
1358
+
1359
+ // ── 3. Try to auto-insert badge into README ──
1360
+ let badgeInserted = false;
1361
+ const readmeCandidates = ['README.md', 'readme.md', 'Readme.md'];
1362
+ for (const name of readmeCandidates) {
1363
+ const readmePath = path.join(cwd, name);
1364
+ if (fs.existsSync(readmePath)) {
1365
+ const content = fs.readFileSync(readmePath, 'utf-8');
1366
+ if (content.includes('Commit Score') || content.includes('poc-backend.amdal-dev.workers.dev/badge')) {
1367
+ console.log(clr(c.dim, ` Badge already in ${name} — skipped`));
1368
+ badgeInserted = true;
1369
+ break;
1370
+ }
1371
+ // Insert after the first H1 heading, or at the top
1372
+ const h1Match = content.match(/^#\s+.+$/m);
1373
+ let newContent;
1374
+ if (h1Match) {
1375
+ const insertPos = h1Match.index + h1Match[0].length;
1376
+ newContent = content.slice(0, insertPos) + '\n\n' + badgeMd + content.slice(insertPos);
1377
+ } else {
1378
+ newContent = badgeMd + '\n\n' + content;
1379
+ }
1380
+ fs.writeFileSync(readmePath, newContent);
1381
+ badgeInserted = true;
1382
+ console.log(clr(c.green, ` ✓ Badge added to ${name}`));
1383
+ break;
1384
+ }
1385
+ }
1386
+ if (!badgeInserted) {
1387
+ console.log(clr(c.dim, ` No README found — paste the badge manually`));
1388
+ }
1389
+
1390
+ // ── 4. Next steps ──
1391
+ console.log(clr(c.bold, '\n What happens next:\n'));
1392
+ if (workflowCreated) {
1393
+ console.log(clr(c.white, ' 1. Commit and push — the Action runs on your next PR'));
1394
+ console.log(clr(c.white, ' 2. PRs with CRITICAL dependencies are blocked automatically'));
1395
+ console.log(clr(c.white, ' 3. The badge updates daily with your project\'s score'));
1396
+ } else {
1397
+ console.log(clr(c.white, ' 1. The badge updates daily with your project\'s score'));
1398
+ console.log(clr(c.white, ' 2. Push to trigger the existing workflow'));
1399
+ }
1400
+ console.log(clr(c.dim, `\n Want daily monitoring + alerts? Get a free key:`));
1401
+ console.log(clr(c.cyan, ' https://getcommit.dev/get-started?utm_source=cli'));
1402
+ console.log(clr(c.dim, ' Then run: ') + clr(c.cyan, 'poc login') + clr(c.dim, ' + ') + clr(c.cyan, 'poc watch <package>\n'));
1403
+ }
1404
+
1254
1405
  async function main() {
1255
1406
  const args = process.argv.slice(2);
1256
1407
 
@@ -1262,6 +1413,11 @@ async function main() {
1262
1413
  // Subcommands
1263
1414
  const subcmd = args[0];
1264
1415
 
1416
+ if (subcmd === 'init') {
1417
+ await cmdInit();
1418
+ process.exit(0);
1419
+ }
1420
+
1265
1421
  if (subcmd === 'login') {
1266
1422
  const keyArg = args[1] || null;
1267
1423
  await cmdLogin(keyArg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proof-of-commitment",
3
- "version": "1.11.0",
3
+ "version": "1.12.1",
4
4
  "description": "Supply chain risk scorer for npm, PyPI, Cargo, and Go packages — behavioral signals that can't be faked",
5
5
  "type": "module",
6
6
  "bin": {